@seed-ship/mcp-ui-solid 6.8.2 → 6.9.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/CHANGELOG.md +34 -0
- package/dist/components/ChartJSRenderer.cjs +27 -13
- package/dist/components/ChartJSRenderer.cjs.map +1 -1
- package/dist/components/ChartJSRenderer.d.ts.map +1 -1
- package/dist/components/ChartJSRenderer.js +28 -14
- package/dist/components/ChartJSRenderer.js.map +1 -1
- package/dist/components/DegradedFallback.cjs +73 -0
- package/dist/components/DegradedFallback.cjs.map +1 -0
- package/dist/components/DegradedFallback.d.ts +37 -0
- package/dist/components/DegradedFallback.d.ts.map +1 -0
- package/dist/components/DegradedFallback.js +73 -0
- package/dist/components/DegradedFallback.js.map +1 -0
- package/dist/components/GraphRenderer.cjs +30 -15
- package/dist/components/GraphRenderer.cjs.map +1 -1
- package/dist/components/GraphRenderer.d.ts.map +1 -1
- package/dist/components/GraphRenderer.js +31 -16
- package/dist/components/GraphRenderer.js.map +1 -1
- package/dist/components/MapRenderer.cjs +128 -107
- package/dist/components/MapRenderer.cjs.map +1 -1
- package/dist/components/MapRenderer.d.ts.map +1 -1
- package/dist/components/MapRenderer.js +129 -108
- package/dist/components/MapRenderer.js.map +1 -1
- package/dist/index.cjs +4 -4
- package/dist/index.js +1 -1
- package/dist/utils/degraded-projections.cjs +87 -0
- package/dist/utils/degraded-projections.cjs.map +1 -0
- package/dist/utils/degraded-projections.d.ts +64 -0
- package/dist/utils/degraded-projections.d.ts.map +1 -0
- package/dist/utils/degraded-projections.js +87 -0
- package/dist/utils/degraded-projections.js.map +1 -0
- package/package.json +1 -1
- package/src/components/ChartJSRenderer.tsx +94 -85
- package/src/components/DegradedFallback.test.tsx +61 -0
- package/src/components/DegradedFallback.tsx +93 -0
- package/src/components/GraphRenderer.tsx +26 -4
- package/src/components/MapRenderer.tsx +446 -392
- package/src/utils/degraded-projections.test.ts +113 -0
- package/src/utils/degraded-projections.ts +149 -0
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MapRenderer.d.ts","sourceRoot":"","sources":["../../src/components/MapRenderer.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,SAAS,EAA+C,MAAM,UAAU,
|
|
1
|
+
{"version":3,"file":"MapRenderer.d.ts","sourceRoot":"","sources":["../../src/components/MapRenderer.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,SAAS,EAA+C,MAAM,UAAU,CAAC;AAElF,OAAO,KAAK,EACV,WAAW,EACX,kBAAkB,EAMnB,MAAM,UAAU,CAAC;AAWlB,MAAM,WAAW,gBAAgB;IAC/B;;OAEG;IACH,SAAS,CAAC,EAAE,WAAW,CAAC;IAExB;;OAEG;IACH,MAAM,CAAC,EAAE,kBAAkB,CAAC;IAE5B;;;OAGG;IACH,cAAc,CAAC,EAAE,OAAO,GAAG,gBAAgB,CAAC;CAC7C;AAiLD,eAAO,MAAM,WAAW,EAAE,SAAS,CAAC,gBAAgB,CAwSnD,CAAC"}
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
import { isServer, createComponent, getNextElement, template, getNextMarker, insert, use, effect, style, className } from "solid-js/web";
|
|
1
|
+
import { isServer, createComponent, getNextElement, template, getNextMarker, insert, mergeProps, use, effect, style, className } from "solid-js/web";
|
|
2
2
|
import { createSignal, createEffect, onCleanup, Show } from "solid-js";
|
|
3
3
|
import { useExpanded, ExpandableWrapper } from "./ExpandableWrapper.js";
|
|
4
|
-
|
|
4
|
+
import { DegradedFallback } from "./DegradedFallback.js";
|
|
5
|
+
import { mapToDegradedTable } from "../utils/degraded-projections.js";
|
|
6
|
+
import { useTelemetry } from "../context/MCPUITelemetryContext.js";
|
|
7
|
+
var _tmpl$ = /* @__PURE__ */ template(`<div class=p-3>`), _tmpl$2 = /* @__PURE__ */ template(`<div>`), _tmpl$3 = /* @__PURE__ */ template(`<div><!$><!/><!$><!/>`);
|
|
5
8
|
let L = null;
|
|
6
9
|
let clusterCssLoaded = false;
|
|
7
10
|
function getChoroplethColor(value, scale, fallback) {
|
|
@@ -127,6 +130,7 @@ const MapRenderer = (props) => {
|
|
|
127
130
|
const [isLeafletLoaded, setIsLeafletLoaded] = createSignal(false);
|
|
128
131
|
const [error, setError] = createSignal(null);
|
|
129
132
|
const isExpanded = useExpanded();
|
|
133
|
+
const telemetry = useTelemetry();
|
|
130
134
|
const params = () => {
|
|
131
135
|
var _a;
|
|
132
136
|
return props.params || ((_a = props.component) == null ? void 0 : _a.params);
|
|
@@ -140,7 +144,7 @@ const MapRenderer = (props) => {
|
|
|
140
144
|
}, 100);
|
|
141
145
|
});
|
|
142
146
|
createEffect(async () => {
|
|
143
|
-
var _a, _b, _c, _d, _e;
|
|
147
|
+
var _a, _b, _c, _d, _e, _f;
|
|
144
148
|
if (isServer) return;
|
|
145
149
|
if (!L) {
|
|
146
150
|
try {
|
|
@@ -182,122 +186,134 @@ const MapRenderer = (props) => {
|
|
|
182
186
|
});
|
|
183
187
|
}
|
|
184
188
|
if (mapInstance && L) {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
});
|
|
192
|
-
const markers = [];
|
|
193
|
-
const shouldCluster = (p == null ? void 0 : p.clustering) && (p == null ? void 0 : p.markers) && p.markers.length > 0;
|
|
194
|
-
if (shouldCluster) {
|
|
195
|
-
try {
|
|
196
|
-
await import("../_virtual/leaflet.markercluster-src.js").then((n) => n.l);
|
|
197
|
-
if (!clusterCssLoaded) {
|
|
198
|
-
await Promise.resolve({ });
|
|
199
|
-
await Promise.resolve({ });
|
|
200
|
-
clusterCssLoaded = true;
|
|
189
|
+
try {
|
|
190
|
+
const p = params();
|
|
191
|
+
const allBoundsLayers = [];
|
|
192
|
+
mapInstance.eachLayer((layer) => {
|
|
193
|
+
if (layer instanceof L.Marker || layer instanceof L.GeoJSON || layer instanceof L.CircleMarker || layer._group || layer._featureGroup) {
|
|
194
|
+
mapInstance.removeLayer(layer);
|
|
201
195
|
}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
clusterGroup.
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
196
|
+
});
|
|
197
|
+
const markers = [];
|
|
198
|
+
const shouldCluster = (p == null ? void 0 : p.clustering) && (p == null ? void 0 : p.markers) && p.markers.length > 0;
|
|
199
|
+
if (shouldCluster) {
|
|
200
|
+
try {
|
|
201
|
+
await import("../_virtual/leaflet.markercluster-src.js").then((n) => n.l);
|
|
202
|
+
if (!clusterCssLoaded) {
|
|
203
|
+
await Promise.resolve({ });
|
|
204
|
+
await Promise.resolve({ });
|
|
205
|
+
clusterCssLoaded = true;
|
|
206
|
+
}
|
|
207
|
+
const clusterOpts = typeof p.clustering === "object" ? p.clustering : {};
|
|
208
|
+
const clusterGroup = L.markerClusterGroup({
|
|
209
|
+
maxClusterRadius: clusterOpts.maxClusterRadius ?? 80,
|
|
210
|
+
spiderfyOnMaxZoom: clusterOpts.spiderfyOnMaxZoom ?? true,
|
|
211
|
+
showCoverageOnHover: clusterOpts.showCoverageOnHover ?? true,
|
|
212
|
+
disableClusteringAtZoom: clusterOpts.disableClusteringAtZoom,
|
|
213
|
+
animate: clusterOpts.animateAddingMarkers ?? true
|
|
214
|
+
});
|
|
215
|
+
(_a = p == null ? void 0 : p.markers) == null ? void 0 : _a.forEach((marker) => {
|
|
216
|
+
const m = L.marker(marker.position);
|
|
217
|
+
if (marker.tooltip) m.bindTooltip(marker.tooltip);
|
|
218
|
+
if (marker.popup) m.bindPopup(marker.popup);
|
|
219
|
+
clusterGroup.addLayer(m);
|
|
220
|
+
markers.push(m);
|
|
221
|
+
});
|
|
222
|
+
mapInstance.addLayer(clusterGroup);
|
|
223
|
+
} catch {
|
|
224
|
+
(_b = p == null ? void 0 : p.markers) == null ? void 0 : _b.forEach((marker) => {
|
|
225
|
+
const m = L.marker(marker.position).addTo(mapInstance);
|
|
226
|
+
if (marker.tooltip) m.bindTooltip(marker.tooltip);
|
|
227
|
+
if (marker.popup) m.bindPopup(marker.popup);
|
|
228
|
+
markers.push(m);
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
} else {
|
|
232
|
+
(_c = p == null ? void 0 : p.markers) == null ? void 0 : _c.forEach((marker) => {
|
|
220
233
|
const m = L.marker(marker.position).addTo(mapInstance);
|
|
221
234
|
if (marker.tooltip) m.bindTooltip(marker.tooltip);
|
|
222
235
|
if (marker.popup) m.bindPopup(marker.popup);
|
|
223
236
|
markers.push(m);
|
|
224
237
|
});
|
|
225
238
|
}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
markers.push(m);
|
|
232
|
-
});
|
|
233
|
-
}
|
|
234
|
-
if (markers.length > 0) {
|
|
235
|
-
allBoundsLayers.push(...markers);
|
|
236
|
-
}
|
|
237
|
-
if (p == null ? void 0 : p.geojson) {
|
|
238
|
-
const geoLayer = addGeoJSONLayer(mapInstance, L, p.geojson, p.geojsonStyle, p.popup);
|
|
239
|
-
allBoundsLayers.push(geoLayer);
|
|
240
|
-
}
|
|
241
|
-
if ((p == null ? void 0 : p.layers) && p.layers.length > 0) {
|
|
242
|
-
const overlays = {};
|
|
243
|
-
for (const layerDef of p.layers) {
|
|
244
|
-
const geoLayer = addGeoJSONLayer(mapInstance, L, layerDef.geojson, layerDef.style || (p == null ? void 0 : p.geojsonStyle), layerDef.popup || (p == null ? void 0 : p.popup));
|
|
245
|
-
overlays[layerDef.name] = geoLayer;
|
|
239
|
+
if (markers.length > 0) {
|
|
240
|
+
allBoundsLayers.push(...markers);
|
|
241
|
+
}
|
|
242
|
+
if (p == null ? void 0 : p.geojson) {
|
|
243
|
+
const geoLayer = addGeoJSONLayer(mapInstance, L, p.geojson, p.geojsonStyle, p.popup);
|
|
246
244
|
allBoundsLayers.push(geoLayer);
|
|
247
|
-
if (layerDef.visible === false) {
|
|
248
|
-
mapInstance.removeLayer(geoLayer);
|
|
249
|
-
}
|
|
250
245
|
}
|
|
251
|
-
if (
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
246
|
+
if ((p == null ? void 0 : p.layers) && p.layers.length > 0) {
|
|
247
|
+
const overlays = {};
|
|
248
|
+
for (const layerDef of p.layers) {
|
|
249
|
+
const geoLayer = addGeoJSONLayer(mapInstance, L, layerDef.geojson, layerDef.style || (p == null ? void 0 : p.geojsonStyle), layerDef.popup || (p == null ? void 0 : p.popup));
|
|
250
|
+
overlays[layerDef.name] = geoLayer;
|
|
251
|
+
allBoundsLayers.push(geoLayer);
|
|
252
|
+
if (layerDef.visible === false) {
|
|
253
|
+
mapInstance.removeLayer(geoLayer);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
if (Object.keys(overlays).length > 1) {
|
|
257
|
+
L.control.layers(null, overlays, {
|
|
258
|
+
collapsed: true
|
|
259
|
+
}).addTo(mapInstance);
|
|
260
|
+
}
|
|
255
261
|
}
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
})
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
})
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
262
|
+
if (p == null ? void 0 : p.pmtiles) {
|
|
263
|
+
try {
|
|
264
|
+
const protomaps = await import(
|
|
265
|
+
/* @vite-ignore */
|
|
266
|
+
"../node_modules/.pnpm/protomaps-leaflet@4.1.1/node_modules/protomaps-leaflet/dist/esm/index.js"
|
|
267
|
+
);
|
|
268
|
+
const pmConfig = p.pmtiles;
|
|
269
|
+
const paintRules = ((_d = pmConfig.paintRules) == null ? void 0 : _d.map((rule) => ({
|
|
270
|
+
dataLayer: rule.dataLayer,
|
|
271
|
+
symbolizer: new protomaps[rule.symbolizer === "polygon" ? "PolygonSymbolizer" : rule.symbolizer === "line" ? "LineSymbolizer" : "CircleSymbolizer"]({
|
|
272
|
+
fill: rule.color || "#3388ff",
|
|
273
|
+
stroke: rule.color || "#333",
|
|
274
|
+
width: rule.width ?? 1,
|
|
275
|
+
opacity: rule.opacity ?? 0.6
|
|
276
|
+
})
|
|
277
|
+
}))) || [];
|
|
278
|
+
const labelRules = ((_e = pmConfig.labelRules) == null ? void 0 : _e.map((rule) => ({
|
|
279
|
+
dataLayer: rule.dataLayer,
|
|
280
|
+
symbolizer: new protomaps.TextSymbolizer({
|
|
281
|
+
label_props: [rule.textField],
|
|
282
|
+
fontSize: rule.fontSize ?? 12
|
|
283
|
+
})
|
|
284
|
+
}))) || [];
|
|
285
|
+
const pmLayer = protomaps.leafletLayer({
|
|
286
|
+
url: pmConfig.url,
|
|
287
|
+
attribution: pmConfig.attribution,
|
|
288
|
+
paintRules,
|
|
289
|
+
labelRules,
|
|
290
|
+
maxZoom: pmConfig.maxZoom,
|
|
291
|
+
minZoom: pmConfig.minZoom
|
|
292
|
+
});
|
|
293
|
+
pmLayer.addTo(mapInstance);
|
|
294
|
+
} catch (e) {
|
|
295
|
+
console.warn("[MCP-UI] Failed to load protomaps-leaflet for PMTiles:", e);
|
|
296
|
+
}
|
|
291
297
|
}
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
+
if ((p == null ? void 0 : p.fitBounds) && allBoundsLayers.length > 0) {
|
|
299
|
+
const group = L.featureGroup(allBoundsLayers);
|
|
300
|
+
const bounds = group.getBounds();
|
|
301
|
+
if (bounds.isValid()) {
|
|
302
|
+
mapInstance.fitBounds(bounds.pad(0.1));
|
|
303
|
+
}
|
|
304
|
+
} else if (p == null ? void 0 : p.center) {
|
|
305
|
+
mapInstance.setView(p.center, p.zoom || mapInstance.getZoom());
|
|
298
306
|
}
|
|
299
|
-
}
|
|
300
|
-
|
|
307
|
+
} catch (err) {
|
|
308
|
+
const message = err instanceof Error ? err.message : "Failed to render map";
|
|
309
|
+
setError(message);
|
|
310
|
+
telemetry == null ? void 0 : telemetry.dispatch({
|
|
311
|
+
type: "render:error",
|
|
312
|
+
errorMessage: message,
|
|
313
|
+
id: ((_f = props.component) == null ? void 0 : _f.id) ?? "",
|
|
314
|
+
componentType: "map",
|
|
315
|
+
ts: Date.now()
|
|
316
|
+
});
|
|
301
317
|
}
|
|
302
318
|
}
|
|
303
319
|
});
|
|
@@ -324,7 +340,12 @@ const MapRenderer = (props) => {
|
|
|
324
340
|
},
|
|
325
341
|
get children() {
|
|
326
342
|
var _el$2 = getNextElement(_tmpl$);
|
|
327
|
-
insert(_el$2,
|
|
343
|
+
insert(_el$2, createComponent(DegradedFallback, mergeProps({
|
|
344
|
+
get message() {
|
|
345
|
+
return `Map rendering failed: ${error()}`;
|
|
346
|
+
},
|
|
347
|
+
caption: "Showing the map data as a coordinate table — the interactive map is unavailable."
|
|
348
|
+
}, () => mapToDegradedTable(params() ?? {}))));
|
|
328
349
|
return _el$2;
|
|
329
350
|
}
|
|
330
351
|
}), _el$5, _co$);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MapRenderer.js","sources":["../../src/components/MapRenderer.tsx"],"sourcesContent":["/**\n * MapRenderer - Interactive map Component\n * Sprint 6: Markers + clustering\n * v3.1.0: GeoJSON, choropleth, popups, multi-layer, PMTiles\n */\n\nimport { Component, createEffect, onCleanup, createSignal, Show } from 'solid-js'\nimport { isServer } from 'solid-js/web'\nimport type { UIComponent, MapComponentParams, MapClusterOptions, MapGeoJSONStyle, MapPopupConfig, MapLayer, MapPMTilesConfig } from '../types'\nimport { ExpandableWrapper, useExpanded } from './ExpandableWrapper'\n\n// Lazy load leaflet (it doesn't support SSR well)\nlet L: any = null\n// Track if marker cluster CSS has been loaded\nlet clusterCssLoaded = false\n\nexport interface MapRendererProps {\n /**\n * UIComponent containing map params\n */\n component?: UIComponent\n\n /**\n * Direct map params\n */\n params?: MapComponentParams\n\n /**\n * Forwarded to the underlying `<ExpandableWrapper>` (v6.3.1).\n * @see ExpandableWrapperProps.toolbarVariant\n */\n toolbarVariant?: 'hover' | 'always-visible'\n}\n\n// ─── Helpers ────────────────────────────────────────────────\n\n/**\n * Resolve choropleth color for a feature based on property value and scale stops.\n */\nfunction getChoroplethColor(\n value: unknown,\n scale: Array<[number, string]>,\n fallback: string\n): string {\n if (value == null || typeof value !== 'number' || !isFinite(value)) return fallback\n\n // Scale is sorted ascending: [[0, '#eff3ff'], [100, '#084594']]\n if (scale.length === 0) return fallback\n if (value <= scale[0][0]) return scale[0][1]\n if (value >= scale[scale.length - 1][0]) return scale[scale.length - 1][1]\n\n // Find surrounding stops and interpolate (use upper bracket color)\n for (let i = 1; i < scale.length; i++) {\n if (value <= scale[i][0]) return scale[i][1]\n }\n return scale[scale.length - 1][1]\n}\n\n/**\n * Build a Leaflet style function from MapGeoJSONStyle config.\n */\nfunction buildStyleFn(style: MapGeoJSONStyle | undefined): (feature: any) => Record<string, unknown> {\n if (!style) {\n return () => ({\n fillColor: '#3388ff',\n fillOpacity: 0.6,\n color: '#333',\n weight: 1,\n opacity: 1,\n })\n }\n\n return (feature: any) => {\n let fillColor = style.fillColor || '#3388ff'\n\n // Choropleth: override fillColor based on feature property\n if (style.choroplethField && style.choroplethScale && feature?.properties) {\n const val = feature.properties[style.choroplethField]\n fillColor = getChoroplethColor(val, style.choroplethScale, style.choroplethFallback || '#ccc')\n }\n\n return {\n fillColor,\n fillOpacity: style.fillOpacity ?? 0.6,\n color: style.strokeColor || '#333',\n weight: style.strokeWeight ?? 1,\n opacity: style.strokeOpacity ?? 1,\n }\n }\n}\n\n/**\n * Build popup HTML from a feature's properties using popup config.\n */\nfunction buildPopupContent(feature: any, popup: MapPopupConfig | undefined): string | null {\n if (!popup || !feature?.properties) return null\n const props = feature.properties\n\n // Custom template\n if (popup.template) {\n return popup.template.replace(/\\{\\{(\\w+)\\}\\}/g, (_, key) => {\n const val = props[key]\n return val != null ? String(val) : ''\n })\n }\n\n // Auto-generated popup\n const parts: string[] = []\n\n if (popup.titleField && props[popup.titleField] != null) {\n parts.push(`<strong>${escapeHtml(String(props[popup.titleField]))}</strong>`)\n }\n\n const fields = popup.fields || Object.keys(props).slice(0, 8)\n for (const key of fields) {\n if (key === popup.titleField) continue\n const val = props[key]\n if (val == null) continue\n const formatted = typeof val === 'number' ? val.toLocaleString('fr-FR') : String(val)\n parts.push(`<span style=\"color:#666;font-size:11px\">${escapeHtml(key)}</span>: ${escapeHtml(formatted)}`)\n }\n\n return parts.join('<br/>')\n}\n\nfunction escapeHtml(str: string): string {\n return str\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n}\n\n/**\n * Add a GeoJSON layer to the map with style and popup support.\n * Returns the layer for bounds calculation.\n */\nfunction addGeoJSONLayer(\n mapInst: any,\n leaflet: any,\n geojson: unknown,\n style?: MapGeoJSONStyle,\n popup?: MapPopupConfig\n): any {\n const styleFn = buildStyleFn(style)\n\n const layer = leaflet.geoJSON(geojson, {\n style: styleFn,\n pointToLayer: (feature: any, latlng: any) => {\n // Render points as circle markers for consistency\n const s = styleFn(feature)\n return leaflet.circleMarker(latlng, {\n radius: 6,\n fillColor: s.fillColor,\n fillOpacity: s.fillOpacity,\n color: s.color,\n weight: s.weight,\n opacity: s.opacity,\n })\n },\n onEachFeature: (feature: any, featureLayer: any) => {\n const html = buildPopupContent(feature, popup)\n if (html) {\n featureLayer.bindPopup(html, { maxWidth: 300 })\n }\n },\n })\n\n layer.addTo(mapInst)\n return layer\n}\n\n// ─── Component ──────────────────────────────────────────────\n\n/**\n * Build a GeoJSON FeatureCollection from the map's `markers` (and any\n * inlined GeoJSON layers, when present). Used by the \"Copy data\" button\n * shipped via `<ExpandableWrapper>` (v6.2.0). Best-effort — clusters,\n * tile layers, and choropleth-only data don't get round-tripped.\n */\nfunction mapToGeoJSON(p: MapComponentParams | undefined): string {\n if (!p) return '{\"type\":\"FeatureCollection\",\"features\":[]}'\n const features: any[] = []\n for (const marker of p.markers ?? []) {\n const pos: any = marker.position as any\n // Accept both [lat, lng] tuple and {lat, lng} object shapes (v5.0.2 spec)\n const lat = Array.isArray(pos) ? pos[0] : pos?.lat\n const lng = Array.isArray(pos) ? pos[1] : pos?.lng\n if (typeof lat !== 'number' || typeof lng !== 'number') continue\n features.push({\n type: 'Feature',\n geometry: { type: 'Point', coordinates: [lng, lat] },\n properties: {\n ...(marker.tooltip ? { tooltip: marker.tooltip } : {}),\n ...(marker.popup ? { popup: marker.popup } : {}),\n },\n })\n }\n return JSON.stringify({ type: 'FeatureCollection', features }, null, 2)\n}\n\nexport const MapRenderer: Component<MapRendererProps> = (props) => {\n let mapContainer: HTMLDivElement | undefined\n let mapInstance: any = null\n const [isLeafletLoaded, setIsLeafletLoaded] = createSignal(false)\n const [error, setError] = createSignal<string | null>(null)\n const isExpanded = useExpanded()\n\n const params = () => props.params || (props.component?.params as MapComponentParams)\n\n // v6.2.0 — Leaflet has to be told to re-measure when its container\n // resizes (e.g. transitioning to fullscreen via ExpandableWrapper).\n // We give the DOM a tick to settle the new dimensions, then ask\n // Leaflet to reflow tiles.\n createEffect(() => {\n const expanded = isExpanded()\n if (!mapInstance) return\n // Read the signal so the effect re-runs on toggle ; the value is\n // observed for its side effects on layout.\n void expanded\n setTimeout(() => mapInstance?.invalidateSize?.(), 100)\n })\n\n // Initialize Map\n createEffect(async () => {\n if (isServer) return // Don't run on server\n\n if (!L) {\n try {\n const module = await import('leaflet')\n L = module.default || module\n await import('leaflet/dist/leaflet.css') // Import CSS\n setIsLeafletLoaded(true)\n } catch (e) {\n console.warn('Failed to load leaflet', e)\n setError('Map library could not be loaded.')\n return\n }\n } else {\n setIsLeafletLoaded(true)\n }\n\n if (isLeafletLoaded() && mapContainer && !mapInstance) {\n const p = params()\n const center = p?.center || [51.505, -0.09] // Default to London\n const zoom = p?.zoom || 13\n\n mapInstance = L.map(mapContainer, {\n zoomControl: p?.zoomControl !== false,\n scrollWheelZoom: p?.scrollWheelZoom !== false,\n attributionControl: false\n }).setView(center, zoom)\n\n // Add OpenStreetMap tile layer\n const tileLayerUrl = p?.tileLayer || 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png'\n L.tileLayer(tileLayerUrl, {\n attribution: p?.attribution || '© <a href=\"https://www.openstreetmap.org/copyright\">OpenStreetMap</a> contributors'\n }).addTo(mapInstance)\n\n if (p?.attribution !== '') {\n L.control.attribution({ prefix: false }).addTo(mapInstance)\n }\n\n // Fix marker icons (Leaflet issue with bundlers)\n delete (L.Icon.Default.prototype as any)._getIconUrl\n L.Icon.Default.mergeOptions({\n iconRetinaUrl: 'https://unpkg.com/leaflet@1.9.4/dist/images/marker-icon-2x.png',\n iconUrl: 'https://unpkg.com/leaflet@1.9.4/dist/images/marker-icon.png',\n shadowUrl: 'https://unpkg.com/leaflet@1.9.4/dist/images/marker-shadow.png',\n })\n }\n\n // Update markers and view\n if (mapInstance && L) {\n const p = params()\n const allBoundsLayers: any[] = []\n\n // Clear existing layers (markers, cluster groups, GeoJSON)\n mapInstance.eachLayer((layer: any) => {\n if (layer instanceof L.Marker || layer instanceof L.GeoJSON\n || layer instanceof L.CircleMarker\n || layer._group || layer._featureGroup) {\n mapInstance.removeLayer(layer)\n }\n })\n\n // ─── Markers (legacy) ────────────────────────\n const markers: any[] = []\n const shouldCluster = p?.clustering && p?.markers && p.markers.length > 0\n\n if (shouldCluster) {\n try {\n await import('leaflet.markercluster')\n if (!clusterCssLoaded) {\n await import('leaflet.markercluster/dist/MarkerCluster.css')\n await import('leaflet.markercluster/dist/MarkerCluster.Default.css')\n clusterCssLoaded = true\n }\n const clusterOpts: MapClusterOptions = typeof p.clustering === 'object' ? p.clustering : {}\n const clusterGroup = (L as any).markerClusterGroup({\n maxClusterRadius: clusterOpts.maxClusterRadius ?? 80,\n spiderfyOnMaxZoom: clusterOpts.spiderfyOnMaxZoom ?? true,\n showCoverageOnHover: clusterOpts.showCoverageOnHover ?? true,\n disableClusteringAtZoom: clusterOpts.disableClusteringAtZoom,\n animate: clusterOpts.animateAddingMarkers ?? true\n })\n p?.markers?.forEach(marker => {\n const m = L.marker(marker.position)\n if (marker.tooltip) m.bindTooltip(marker.tooltip)\n if (marker.popup) m.bindPopup(marker.popup)\n clusterGroup.addLayer(m)\n markers.push(m)\n })\n mapInstance.addLayer(clusterGroup)\n } catch {\n p?.markers?.forEach(marker => {\n const m = L.marker(marker.position).addTo(mapInstance)\n if (marker.tooltip) m.bindTooltip(marker.tooltip)\n if (marker.popup) m.bindPopup(marker.popup)\n markers.push(m)\n })\n }\n } else {\n p?.markers?.forEach(marker => {\n const m = L.marker(marker.position).addTo(mapInstance)\n if (marker.tooltip) m.bindTooltip(marker.tooltip)\n if (marker.popup) m.bindPopup(marker.popup)\n markers.push(m)\n })\n }\n\n if (markers.length > 0) {\n allBoundsLayers.push(...markers)\n }\n\n // ─── GeoJSON (v3.1.0) ───────────────────────\n if (p?.geojson) {\n const geoLayer = addGeoJSONLayer(mapInstance, L, p.geojson, p.geojsonStyle, p.popup)\n allBoundsLayers.push(geoLayer)\n }\n\n // ─── Named layers (v3.1.0) ──────────────────\n if (p?.layers && p.layers.length > 0) {\n const overlays: Record<string, any> = {}\n\n for (const layerDef of p.layers) {\n const geoLayer = addGeoJSONLayer(\n mapInstance, L,\n layerDef.geojson,\n layerDef.style || p?.geojsonStyle,\n layerDef.popup || p?.popup\n )\n\n overlays[layerDef.name] = geoLayer\n allBoundsLayers.push(geoLayer)\n\n // Respect initial visibility\n if (layerDef.visible === false) {\n mapInstance.removeLayer(geoLayer)\n }\n }\n\n // Add layer control if multiple layers\n if (Object.keys(overlays).length > 1) {\n L.control.layers(null, overlays, { collapsed: true }).addTo(mapInstance)\n }\n }\n\n // ─── PMTiles (v3.1.0) ────────────────────────\n if (p?.pmtiles) {\n try {\n // @ts-ignore — optional peer dependency, may not be installed\n const protomaps = await import(/* @vite-ignore */ 'protomaps-leaflet')\n const pmConfig = p.pmtiles\n\n const paintRules = pmConfig.paintRules?.map(rule => ({\n dataLayer: rule.dataLayer,\n symbolizer: new (protomaps as any)[\n rule.symbolizer === 'polygon' ? 'PolygonSymbolizer' :\n rule.symbolizer === 'line' ? 'LineSymbolizer' :\n 'CircleSymbolizer'\n ]({\n fill: rule.color || '#3388ff',\n stroke: rule.color || '#333',\n width: rule.width ?? 1,\n opacity: rule.opacity ?? 0.6,\n }),\n })) || []\n\n const labelRules = pmConfig.labelRules?.map(rule => ({\n dataLayer: rule.dataLayer,\n symbolizer: new (protomaps as any).TextSymbolizer({\n label_props: [rule.textField],\n fontSize: rule.fontSize ?? 12,\n }),\n })) || []\n\n const pmLayer = (protomaps as any).leafletLayer({\n url: pmConfig.url,\n attribution: pmConfig.attribution,\n paintRules,\n labelRules,\n maxZoom: pmConfig.maxZoom,\n minZoom: pmConfig.minZoom,\n })\n\n pmLayer.addTo(mapInstance)\n } catch (e) {\n console.warn('[MCP-UI] Failed to load protomaps-leaflet for PMTiles:', e)\n }\n }\n\n // ─── Fit bounds ─────────────────────────────\n if (p?.fitBounds && allBoundsLayers.length > 0) {\n const group = L.featureGroup(allBoundsLayers)\n const bounds = group.getBounds()\n if (bounds.isValid()) {\n mapInstance.fitBounds(bounds.pad(0.1))\n }\n } else if (p?.center) {\n mapInstance.setView(p.center, p.zoom || mapInstance.getZoom())\n }\n }\n })\n\n // Cleanup\n onCleanup(() => {\n if (mapInstance) {\n mapInstance.remove()\n mapInstance = null\n }\n })\n\n return (\n <ExpandableWrapper\n title={'Map'}\n copyData={mapToGeoJSON(params())}\n copyLabel=\"Copy markers as GeoJSON\"\n toolbarVariant={props.toolbarVariant}\n >\n <div class={`w-full bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden ${params()?.className || ''} ${\n isExpanded() ? 'flex-1 min-h-0 flex flex-col' : ''\n }`}>\n <Show when={error()}>\n <div class=\"p-4 text-red-500 bg-red-50 dark:bg-red-900/20 text-center\">\n {error()}\n </div>\n </Show>\n <Show when={!error()}>\n <div\n ref={mapContainer}\n style={\n isExpanded()\n ? { height: '100%', width: '100%', 'z-index': 0 }\n : { height: params()?.height || '400px', width: '100%', 'z-index': 0 }\n }\n class={`relative z-0 ${isExpanded() ? 'flex-1 min-h-0' : ''}`}\n />\n </Show>\n </div>\n </ExpandableWrapper>\n )\n}\n"],"names":["L","clusterCssLoaded","getChoroplethColor","value","scale","fallback","isFinite","length","i","buildStyleFn","style","fillColor","fillOpacity","color","weight","opacity","feature","choroplethField","choroplethScale","properties","val","choroplethFallback","strokeColor","strokeWeight","strokeOpacity","buildPopupContent","popup","props","template","replace","_","key","String","parts","titleField","push","escapeHtml","fields","Object","keys","slice","formatted","toLocaleString","join","str","addGeoJSONLayer","mapInst","leaflet","geojson","styleFn","layer","geoJSON","pointToLayer","latlng","s","circleMarker","radius","onEachFeature","featureLayer","html","bindPopup","maxWidth","addTo","mapToGeoJSON","p","features","marker","markers","pos","position","lat","Array","isArray","lng","type","geometry","coordinates","tooltip","JSON","stringify","MapRenderer","mapContainer","mapInstance","isLeafletLoaded","setIsLeafletLoaded","createSignal","error","setError","isExpanded","useExpanded","params","component","createEffect","setTimeout","invalidateSize","isServer","module","default","e","console","warn","center","zoom","map","zoomControl","scrollWheelZoom","attributionControl","setView","tileLayerUrl","tileLayer","attribution","control","prefix","Icon","Default","prototype","_getIconUrl","mergeOptions","iconRetinaUrl","iconUrl","shadowUrl","allBoundsLayers","eachLayer","Marker","GeoJSON","CircleMarker","_group","_featureGroup","removeLayer","shouldCluster","clustering","clusterOpts","clusterGroup","markerClusterGroup","maxClusterRadius","spiderfyOnMaxZoom","showCoverageOnHover","disableClusteringAtZoom","animate","animateAddingMarkers","forEach","m","bindTooltip","addLayer","geoLayer","geojsonStyle","layers","overlays","layerDef","name","visible","collapsed","pmtiles","protomaps","pmConfig","paintRules","rule","dataLayer","symbolizer","fill","stroke","width","labelRules","TextSymbolizer","label_props","textField","fontSize","pmLayer","leafletLayer","url","maxZoom","minZoom","fitBounds","group","featureGroup","bounds","getBounds","isValid","pad","getZoom","onCleanup","remove","_$createComponent","ExpandableWrapper","title","copyData","copyLabel","toolbarVariant","children","_el$","_$getNextElement","_tmpl$3","_el$4","firstChild","_el$5","_co$","_$getNextMarker","nextSibling","_el$6","_el$7","_co$2","_$insert","Show","when","_el$2","_tmpl$","_el$3","_tmpl$2","_ref$","_$use","_$effect","_p$","_v$","height","_v$2","_$style","t","_$className","undefined","className"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"MapRenderer.js","sources":["../../src/components/MapRenderer.tsx"],"sourcesContent":["/**\n * MapRenderer - Interactive map Component\n * Sprint 6: Markers + clustering\n * v3.1.0: GeoJSON, choropleth, popups, multi-layer, PMTiles\n */\n\nimport { Component, createEffect, onCleanup, createSignal, Show } from 'solid-js';\nimport { isServer } from 'solid-js/web';\nimport type {\n UIComponent,\n MapComponentParams,\n MapClusterOptions,\n MapGeoJSONStyle,\n MapPopupConfig,\n MapLayer,\n MapPMTilesConfig,\n} from '../types';\nimport { ExpandableWrapper, useExpanded } from './ExpandableWrapper';\nimport { DegradedFallback } from './DegradedFallback';\nimport { mapToDegradedTable } from '../utils/degraded-projections';\nimport { useTelemetry } from '../context/MCPUITelemetryContext';\n\n// Lazy load leaflet (it doesn't support SSR well)\nlet L: any = null;\n// Track if marker cluster CSS has been loaded\nlet clusterCssLoaded = false;\n\nexport interface MapRendererProps {\n /**\n * UIComponent containing map params\n */\n component?: UIComponent;\n\n /**\n * Direct map params\n */\n params?: MapComponentParams;\n\n /**\n * Forwarded to the underlying `<ExpandableWrapper>` (v6.3.1).\n * @see ExpandableWrapperProps.toolbarVariant\n */\n toolbarVariant?: 'hover' | 'always-visible';\n}\n\n// ─── Helpers ────────────────────────────────────────────────\n\n/**\n * Resolve choropleth color for a feature based on property value and scale stops.\n */\nfunction getChoroplethColor(\n value: unknown,\n scale: Array<[number, string]>,\n fallback: string\n): string {\n if (value == null || typeof value !== 'number' || !isFinite(value)) return fallback;\n\n // Scale is sorted ascending: [[0, '#eff3ff'], [100, '#084594']]\n if (scale.length === 0) return fallback;\n if (value <= scale[0][0]) return scale[0][1];\n if (value >= scale[scale.length - 1][0]) return scale[scale.length - 1][1];\n\n // Find surrounding stops and interpolate (use upper bracket color)\n for (let i = 1; i < scale.length; i++) {\n if (value <= scale[i][0]) return scale[i][1];\n }\n return scale[scale.length - 1][1];\n}\n\n/**\n * Build a Leaflet style function from MapGeoJSONStyle config.\n */\nfunction buildStyleFn(\n style: MapGeoJSONStyle | undefined\n): (feature: any) => Record<string, unknown> {\n if (!style) {\n return () => ({\n fillColor: '#3388ff',\n fillOpacity: 0.6,\n color: '#333',\n weight: 1,\n opacity: 1,\n });\n }\n\n return (feature: any) => {\n let fillColor = style.fillColor || '#3388ff';\n\n // Choropleth: override fillColor based on feature property\n if (style.choroplethField && style.choroplethScale && feature?.properties) {\n const val = feature.properties[style.choroplethField];\n fillColor = getChoroplethColor(\n val,\n style.choroplethScale,\n style.choroplethFallback || '#ccc'\n );\n }\n\n return {\n fillColor,\n fillOpacity: style.fillOpacity ?? 0.6,\n color: style.strokeColor || '#333',\n weight: style.strokeWeight ?? 1,\n opacity: style.strokeOpacity ?? 1,\n };\n };\n}\n\n/**\n * Build popup HTML from a feature's properties using popup config.\n */\nfunction buildPopupContent(feature: any, popup: MapPopupConfig | undefined): string | null {\n if (!popup || !feature?.properties) return null;\n const props = feature.properties;\n\n // Custom template\n if (popup.template) {\n return popup.template.replace(/\\{\\{(\\w+)\\}\\}/g, (_, key) => {\n const val = props[key];\n return val != null ? String(val) : '';\n });\n }\n\n // Auto-generated popup\n const parts: string[] = [];\n\n if (popup.titleField && props[popup.titleField] != null) {\n parts.push(`<strong>${escapeHtml(String(props[popup.titleField]))}</strong>`);\n }\n\n const fields = popup.fields || Object.keys(props).slice(0, 8);\n for (const key of fields) {\n if (key === popup.titleField) continue;\n const val = props[key];\n if (val == null) continue;\n const formatted = typeof val === 'number' ? val.toLocaleString('fr-FR') : String(val);\n parts.push(\n `<span style=\"color:#666;font-size:11px\">${escapeHtml(key)}</span>: ${escapeHtml(formatted)}`\n );\n }\n\n return parts.join('<br/>');\n}\n\nfunction escapeHtml(str: string): string {\n return str\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"');\n}\n\n/**\n * Add a GeoJSON layer to the map with style and popup support.\n * Returns the layer for bounds calculation.\n */\nfunction addGeoJSONLayer(\n mapInst: any,\n leaflet: any,\n geojson: unknown,\n style?: MapGeoJSONStyle,\n popup?: MapPopupConfig\n): any {\n const styleFn = buildStyleFn(style);\n\n const layer = leaflet.geoJSON(geojson, {\n style: styleFn,\n pointToLayer: (feature: any, latlng: any) => {\n // Render points as circle markers for consistency\n const s = styleFn(feature);\n return leaflet.circleMarker(latlng, {\n radius: 6,\n fillColor: s.fillColor,\n fillOpacity: s.fillOpacity,\n color: s.color,\n weight: s.weight,\n opacity: s.opacity,\n });\n },\n onEachFeature: (feature: any, featureLayer: any) => {\n const html = buildPopupContent(feature, popup);\n if (html) {\n featureLayer.bindPopup(html, { maxWidth: 300 });\n }\n },\n });\n\n layer.addTo(mapInst);\n return layer;\n}\n\n// ─── Component ──────────────────────────────────────────────\n\n/**\n * Build a GeoJSON FeatureCollection from the map's `markers` (and any\n * inlined GeoJSON layers, when present). Used by the \"Copy data\" button\n * shipped via `<ExpandableWrapper>` (v6.2.0). Best-effort — clusters,\n * tile layers, and choropleth-only data don't get round-tripped.\n */\nfunction mapToGeoJSON(p: MapComponentParams | undefined): string {\n if (!p) return '{\"type\":\"FeatureCollection\",\"features\":[]}';\n const features: any[] = [];\n for (const marker of p.markers ?? []) {\n const pos: any = marker.position as any;\n // Accept both [lat, lng] tuple and {lat, lng} object shapes (v5.0.2 spec)\n const lat = Array.isArray(pos) ? pos[0] : pos?.lat;\n const lng = Array.isArray(pos) ? pos[1] : pos?.lng;\n if (typeof lat !== 'number' || typeof lng !== 'number') continue;\n features.push({\n type: 'Feature',\n geometry: { type: 'Point', coordinates: [lng, lat] },\n properties: {\n ...(marker.tooltip ? { tooltip: marker.tooltip } : {}),\n ...(marker.popup ? { popup: marker.popup } : {}),\n },\n });\n }\n return JSON.stringify({ type: 'FeatureCollection', features }, null, 2);\n}\n\nexport const MapRenderer: Component<MapRendererProps> = (props) => {\n let mapContainer: HTMLDivElement | undefined;\n let mapInstance: any = null;\n const [isLeafletLoaded, setIsLeafletLoaded] = createSignal(false);\n const [error, setError] = createSignal<string | null>(null);\n const isExpanded = useExpanded();\n const telemetry = useTelemetry();\n\n const params = () => props.params || (props.component?.params as MapComponentParams);\n\n // v6.2.0 — Leaflet has to be told to re-measure when its container\n // resizes (e.g. transitioning to fullscreen via ExpandableWrapper).\n // We give the DOM a tick to settle the new dimensions, then ask\n // Leaflet to reflow tiles.\n createEffect(() => {\n const expanded = isExpanded();\n if (!mapInstance) return;\n // Read the signal so the effect re-runs on toggle ; the value is\n // observed for its side effects on layout.\n void expanded;\n setTimeout(() => mapInstance?.invalidateSize?.(), 100);\n });\n\n // Initialize Map\n createEffect(async () => {\n if (isServer) return; // Don't run on server\n\n if (!L) {\n try {\n const module = await import('leaflet');\n L = module.default || module;\n await import('leaflet/dist/leaflet.css'); // Import CSS\n setIsLeafletLoaded(true);\n } catch (e) {\n console.warn('Failed to load leaflet', e);\n setError('Map library could not be loaded.');\n return;\n }\n } else {\n setIsLeafletLoaded(true);\n }\n\n if (isLeafletLoaded() && mapContainer && !mapInstance) {\n const p = params();\n const center = p?.center || [51.505, -0.09]; // Default to London\n const zoom = p?.zoom || 13;\n\n mapInstance = L.map(mapContainer, {\n zoomControl: p?.zoomControl !== false,\n scrollWheelZoom: p?.scrollWheelZoom !== false,\n attributionControl: false,\n }).setView(center, zoom);\n\n // Add OpenStreetMap tile layer\n const tileLayerUrl = p?.tileLayer || 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png';\n L.tileLayer(tileLayerUrl, {\n attribution:\n p?.attribution ||\n '© <a href=\"https://www.openstreetmap.org/copyright\">OpenStreetMap</a> contributors',\n }).addTo(mapInstance);\n\n if (p?.attribution !== '') {\n L.control.attribution({ prefix: false }).addTo(mapInstance);\n }\n\n // Fix marker icons (Leaflet issue with bundlers)\n delete (L.Icon.Default.prototype as any)._getIconUrl;\n L.Icon.Default.mergeOptions({\n iconRetinaUrl: 'https://unpkg.com/leaflet@1.9.4/dist/images/marker-icon-2x.png',\n iconUrl: 'https://unpkg.com/leaflet@1.9.4/dist/images/marker-icon.png',\n shadowUrl: 'https://unpkg.com/leaflet@1.9.4/dist/images/marker-shadow.png',\n });\n }\n\n // Update markers and view\n if (mapInstance && L) {\n try {\n const p = params();\n const allBoundsLayers: any[] = [];\n\n // Clear existing layers (markers, cluster groups, GeoJSON)\n mapInstance.eachLayer((layer: any) => {\n if (\n layer instanceof L.Marker ||\n layer instanceof L.GeoJSON ||\n layer instanceof L.CircleMarker ||\n layer._group ||\n layer._featureGroup\n ) {\n mapInstance.removeLayer(layer);\n }\n });\n\n // ─── Markers (legacy) ────────────────────────\n const markers: any[] = [];\n const shouldCluster = p?.clustering && p?.markers && p.markers.length > 0;\n\n if (shouldCluster) {\n try {\n await import('leaflet.markercluster');\n if (!clusterCssLoaded) {\n await import('leaflet.markercluster/dist/MarkerCluster.css');\n await import('leaflet.markercluster/dist/MarkerCluster.Default.css');\n clusterCssLoaded = true;\n }\n const clusterOpts: MapClusterOptions =\n typeof p.clustering === 'object' ? p.clustering : {};\n const clusterGroup = (L as any).markerClusterGroup({\n maxClusterRadius: clusterOpts.maxClusterRadius ?? 80,\n spiderfyOnMaxZoom: clusterOpts.spiderfyOnMaxZoom ?? true,\n showCoverageOnHover: clusterOpts.showCoverageOnHover ?? true,\n disableClusteringAtZoom: clusterOpts.disableClusteringAtZoom,\n animate: clusterOpts.animateAddingMarkers ?? true,\n });\n p?.markers?.forEach((marker) => {\n const m = L.marker(marker.position);\n if (marker.tooltip) m.bindTooltip(marker.tooltip);\n if (marker.popup) m.bindPopup(marker.popup);\n clusterGroup.addLayer(m);\n markers.push(m);\n });\n mapInstance.addLayer(clusterGroup);\n } catch {\n p?.markers?.forEach((marker) => {\n const m = L.marker(marker.position).addTo(mapInstance);\n if (marker.tooltip) m.bindTooltip(marker.tooltip);\n if (marker.popup) m.bindPopup(marker.popup);\n markers.push(m);\n });\n }\n } else {\n p?.markers?.forEach((marker) => {\n const m = L.marker(marker.position).addTo(mapInstance);\n if (marker.tooltip) m.bindTooltip(marker.tooltip);\n if (marker.popup) m.bindPopup(marker.popup);\n markers.push(m);\n });\n }\n\n if (markers.length > 0) {\n allBoundsLayers.push(...markers);\n }\n\n // ─── GeoJSON (v3.1.0) ───────────────────────\n if (p?.geojson) {\n const geoLayer = addGeoJSONLayer(mapInstance, L, p.geojson, p.geojsonStyle, p.popup);\n allBoundsLayers.push(geoLayer);\n }\n\n // ─── Named layers (v3.1.0) ──────────────────\n if (p?.layers && p.layers.length > 0) {\n const overlays: Record<string, any> = {};\n\n for (const layerDef of p.layers) {\n const geoLayer = addGeoJSONLayer(\n mapInstance,\n L,\n layerDef.geojson,\n layerDef.style || p?.geojsonStyle,\n layerDef.popup || p?.popup\n );\n\n overlays[layerDef.name] = geoLayer;\n allBoundsLayers.push(geoLayer);\n\n // Respect initial visibility\n if (layerDef.visible === false) {\n mapInstance.removeLayer(geoLayer);\n }\n }\n\n // Add layer control if multiple layers\n if (Object.keys(overlays).length > 1) {\n L.control.layers(null, overlays, { collapsed: true }).addTo(mapInstance);\n }\n }\n\n // ─── PMTiles (v3.1.0) ────────────────────────\n if (p?.pmtiles) {\n try {\n // @ts-ignore — optional peer dependency, may not be installed\n const protomaps = await import(/* @vite-ignore */ 'protomaps-leaflet');\n const pmConfig = p.pmtiles;\n\n const paintRules =\n pmConfig.paintRules?.map((rule) => ({\n dataLayer: rule.dataLayer,\n symbolizer: new (protomaps as any)[\n rule.symbolizer === 'polygon'\n ? 'PolygonSymbolizer'\n : rule.symbolizer === 'line'\n ? 'LineSymbolizer'\n : 'CircleSymbolizer'\n ]({\n fill: rule.color || '#3388ff',\n stroke: rule.color || '#333',\n width: rule.width ?? 1,\n opacity: rule.opacity ?? 0.6,\n }),\n })) || [];\n\n const labelRules =\n pmConfig.labelRules?.map((rule) => ({\n dataLayer: rule.dataLayer,\n symbolizer: new (protomaps as any).TextSymbolizer({\n label_props: [rule.textField],\n fontSize: rule.fontSize ?? 12,\n }),\n })) || [];\n\n const pmLayer = (protomaps as any).leafletLayer({\n url: pmConfig.url,\n attribution: pmConfig.attribution,\n paintRules,\n labelRules,\n maxZoom: pmConfig.maxZoom,\n minZoom: pmConfig.minZoom,\n });\n\n pmLayer.addTo(mapInstance);\n } catch (e) {\n console.warn('[MCP-UI] Failed to load protomaps-leaflet for PMTiles:', e);\n }\n }\n\n // ─── Fit bounds ─────────────────────────────\n if (p?.fitBounds && allBoundsLayers.length > 0) {\n const group = L.featureGroup(allBoundsLayers);\n const bounds = group.getBounds();\n if (bounds.isValid()) {\n mapInstance.fitBounds(bounds.pad(0.1));\n }\n } else if (p?.center) {\n mapInstance.setView(p.center, p.zoom || mapInstance.getZoom());\n }\n } catch (err) {\n // Fallback ladder (P2.5): a Leaflet drawing failure degrades to\n // the coordinate table below instead of a blank/partial map.\n const message = err instanceof Error ? err.message : 'Failed to render map';\n setError(message);\n telemetry?.dispatch({\n type: 'render:error',\n errorMessage: message,\n id: props.component?.id ?? '',\n componentType: 'map',\n ts: Date.now(),\n });\n }\n }\n });\n\n // Cleanup\n onCleanup(() => {\n if (mapInstance) {\n mapInstance.remove();\n mapInstance = null;\n }\n });\n\n return (\n <ExpandableWrapper\n title={'Map'}\n copyData={mapToGeoJSON(params())}\n copyLabel=\"Copy markers as GeoJSON\"\n toolbarVariant={props.toolbarVariant}\n >\n <div\n class={`w-full bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden ${params()?.className || ''} ${\n isExpanded() ? 'flex-1 min-h-0 flex flex-col' : ''\n }`}\n >\n <Show when={error()}>\n {/* Fallback ladder (P2.5): degrade to a coordinate table\n rather than a bare error string. */}\n <div class=\"p-3\">\n <DegradedFallback\n message={`Map rendering failed: ${error()}`}\n caption=\"Showing the map data as a coordinate table — the interactive map is unavailable.\"\n {...mapToDegradedTable(params() ?? {})}\n />\n </div>\n </Show>\n <Show when={!error()}>\n <div\n ref={mapContainer}\n style={\n isExpanded()\n ? { height: '100%', width: '100%', 'z-index': 0 }\n : { height: params()?.height || '400px', width: '100%', 'z-index': 0 }\n }\n class={`relative z-0 ${isExpanded() ? 'flex-1 min-h-0' : ''}`}\n />\n </Show>\n </div>\n </ExpandableWrapper>\n );\n};\n"],"names":["L","clusterCssLoaded","getChoroplethColor","value","scale","fallback","isFinite","length","i","buildStyleFn","style","fillColor","fillOpacity","color","weight","opacity","feature","choroplethField","choroplethScale","properties","val","choroplethFallback","strokeColor","strokeWeight","strokeOpacity","buildPopupContent","popup","props","template","replace","_","key","String","parts","titleField","push","escapeHtml","fields","Object","keys","slice","formatted","toLocaleString","join","str","addGeoJSONLayer","mapInst","leaflet","geojson","styleFn","layer","geoJSON","pointToLayer","latlng","s","circleMarker","radius","onEachFeature","featureLayer","html","bindPopup","maxWidth","addTo","mapToGeoJSON","p","features","marker","markers","pos","position","lat","Array","isArray","lng","type","geometry","coordinates","tooltip","JSON","stringify","MapRenderer","mapContainer","mapInstance","isLeafletLoaded","setIsLeafletLoaded","createSignal","error","setError","isExpanded","useExpanded","telemetry","useTelemetry","params","component","createEffect","setTimeout","invalidateSize","isServer","module","default","e","console","warn","center","zoom","map","zoomControl","scrollWheelZoom","attributionControl","setView","tileLayerUrl","tileLayer","attribution","control","prefix","Icon","Default","prototype","_getIconUrl","mergeOptions","iconRetinaUrl","iconUrl","shadowUrl","allBoundsLayers","eachLayer","Marker","GeoJSON","CircleMarker","_group","_featureGroup","removeLayer","shouldCluster","clustering","clusterOpts","clusterGroup","markerClusterGroup","maxClusterRadius","spiderfyOnMaxZoom","showCoverageOnHover","disableClusteringAtZoom","animate","animateAddingMarkers","forEach","m","bindTooltip","addLayer","geoLayer","geojsonStyle","layers","overlays","layerDef","name","visible","collapsed","pmtiles","protomaps","pmConfig","paintRules","rule","dataLayer","symbolizer","fill","stroke","width","labelRules","TextSymbolizer","label_props","textField","fontSize","pmLayer","leafletLayer","url","maxZoom","minZoom","fitBounds","group","featureGroup","bounds","getBounds","isValid","pad","getZoom","err","message","Error","dispatch","errorMessage","id","componentType","ts","Date","now","onCleanup","remove","_$createComponent","ExpandableWrapper","title","copyData","copyLabel","toolbarVariant","children","_el$","_$getNextElement","_tmpl$3","_el$4","firstChild","_el$5","_co$","_$getNextMarker","nextSibling","_el$6","_el$7","_co$2","_$insert","Show","when","_el$2","_tmpl$","DegradedFallback","_$mergeProps","caption","mapToDegradedTable","_el$3","_tmpl$2","_ref$","_$use","_$effect","_p$","_v$","height","_v$2","_$style","t","_$className","undefined","className"],"mappings":"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"}
|
package/dist/index.cjs
CHANGED
|
@@ -18,6 +18,7 @@ require("./components/LightboxOverlay.cjs");
|
|
|
18
18
|
require("./components/ImageGalleryRenderer.cjs");
|
|
19
19
|
const ExpandableWrapper = require("./components/ExpandableWrapper.cjs");
|
|
20
20
|
require("./components/CodeBlockRenderer.cjs");
|
|
21
|
+
const MCPUITelemetryContext = require("./context/MCPUITelemetryContext.cjs");
|
|
21
22
|
const AgentCard = require("./components/AgentCard.cjs");
|
|
22
23
|
const SplitStepper = require("./components/SplitStepper.cjs");
|
|
23
24
|
const AgentHandoff = require("./components/AgentHandoff.cjs");
|
|
@@ -45,7 +46,6 @@ const perf = require("./utils/perf.cjs");
|
|
|
45
46
|
const stableKey = require("./utils/stable-key.cjs");
|
|
46
47
|
const duplicateMountRegistry = require("./utils/duplicate-mount-registry.cjs");
|
|
47
48
|
const MCPUIStringsContext = require("./context/MCPUIStringsContext.cjs");
|
|
48
|
-
const MCPUITelemetryContext = require("./context/MCPUITelemetryContext.cjs");
|
|
49
49
|
const telemetry = require("./services/telemetry.cjs");
|
|
50
50
|
const useStreamingUI = require("./hooks/useStreamingUI.cjs");
|
|
51
51
|
const useAction = require("./hooks/useAction.cjs");
|
|
@@ -75,6 +75,9 @@ exports.PresentationFeedback = PresentationFeedback.PresentationFeedback;
|
|
|
75
75
|
exports.GenerativeUIErrorBoundary = GenerativeUIErrorBoundary.GenerativeUIErrorBoundary;
|
|
76
76
|
exports.ExpandableWrapper = ExpandableWrapper.ExpandableWrapper;
|
|
77
77
|
exports.useExpanded = ExpandableWrapper.useExpanded;
|
|
78
|
+
exports.MCPUITelemetryContext = MCPUITelemetryContext.MCPUITelemetryContext;
|
|
79
|
+
exports.MCPUITelemetryProvider = MCPUITelemetryContext.MCPUITelemetryProvider;
|
|
80
|
+
exports.useTelemetry = MCPUITelemetryContext.useTelemetry;
|
|
78
81
|
exports.AgentCard = AgentCard.AgentCard;
|
|
79
82
|
exports.AgentStatusBadge = AgentCard.AgentStatusBadge;
|
|
80
83
|
exports.SplitStepper = SplitStepper.SplitStepper;
|
|
@@ -122,9 +125,6 @@ exports.DEFAULT_MCPUI_STRINGS = MCPUIStringsContext.DEFAULT_MCPUI_STRINGS;
|
|
|
122
125
|
exports.MCPUIStringsContext = MCPUIStringsContext.MCPUIStringsContext;
|
|
123
126
|
exports.MCPUIStringsProvider = MCPUIStringsContext.MCPUIStringsProvider;
|
|
124
127
|
exports.useMCPUIStrings = MCPUIStringsContext.useMCPUIStrings;
|
|
125
|
-
exports.MCPUITelemetryContext = MCPUITelemetryContext.MCPUITelemetryContext;
|
|
126
|
-
exports.MCPUITelemetryProvider = MCPUITelemetryContext.MCPUITelemetryProvider;
|
|
127
|
-
exports.useTelemetry = MCPUITelemetryContext.useTelemetry;
|
|
128
128
|
exports.createTelemetryDispatcher = telemetry.createTelemetryDispatcher;
|
|
129
129
|
exports.useStreamingUI = useStreamingUI.useStreamingUI;
|
|
130
130
|
exports.useAction = useAction.useAction;
|
package/dist/index.js
CHANGED
|
@@ -16,6 +16,7 @@ import "./components/LightboxOverlay.js";
|
|
|
16
16
|
import "./components/ImageGalleryRenderer.js";
|
|
17
17
|
import { ExpandableWrapper, useExpanded } from "./components/ExpandableWrapper.js";
|
|
18
18
|
import "./components/CodeBlockRenderer.js";
|
|
19
|
+
import { MCPUITelemetryContext, MCPUITelemetryProvider, useTelemetry } from "./context/MCPUITelemetryContext.js";
|
|
19
20
|
import { AgentCard, AgentStatusBadge } from "./components/AgentCard.js";
|
|
20
21
|
import { SplitStepper } from "./components/SplitStepper.js";
|
|
21
22
|
import { AgentHandoff } from "./components/AgentHandoff.js";
|
|
@@ -43,7 +44,6 @@ import { PERF_PREFIX, markRenderEnd, markRenderStart } from "./utils/perf.js";
|
|
|
43
44
|
import { getUiResourceStableKey } from "./utils/stable-key.js";
|
|
44
45
|
import { setDuplicateMountReporter } from "./utils/duplicate-mount-registry.js";
|
|
45
46
|
import { DEFAULT_MCPUI_STRINGS, MCPUIStringsContext, MCPUIStringsProvider, useMCPUIStrings } from "./context/MCPUIStringsContext.js";
|
|
46
|
-
import { MCPUITelemetryContext, MCPUITelemetryProvider, useTelemetry } from "./context/MCPUITelemetryContext.js";
|
|
47
47
|
import { createTelemetryDispatcher } from "./services/telemetry.js";
|
|
48
48
|
import { useStreamingUI } from "./hooks/useStreamingUI.js";
|
|
49
49
|
import { useAction, useToolAction } from "./hooks/useAction.js";
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const MAX_PROJECTED_ROWS = 200;
|
|
4
|
+
function cell(value) {
|
|
5
|
+
if (value == null) return "";
|
|
6
|
+
if (typeof value === "object") {
|
|
7
|
+
try {
|
|
8
|
+
return JSON.stringify(value);
|
|
9
|
+
} catch {
|
|
10
|
+
return String(value);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
return String(value);
|
|
14
|
+
}
|
|
15
|
+
function graphToDegradedTable(params) {
|
|
16
|
+
const edges = params.edges ?? [];
|
|
17
|
+
if (edges.length > 0) {
|
|
18
|
+
return {
|
|
19
|
+
columns: ["Source", "Target", "Label"],
|
|
20
|
+
rows: edges.slice(0, MAX_PROJECTED_ROWS).map((e) => {
|
|
21
|
+
const label = [e.weight != null ? String(e.weight) : "", e.label ?? ""].filter(Boolean).join(" · ");
|
|
22
|
+
return [cell(e.source), cell(e.target), label];
|
|
23
|
+
})
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
const nodes = params.nodes ?? [];
|
|
27
|
+
return {
|
|
28
|
+
columns: ["Node", "Label"],
|
|
29
|
+
rows: nodes.slice(0, MAX_PROJECTED_ROWS).map((n) => [cell(n.id), cell(n.label ?? n.id)])
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
function firstLngLat(coords) {
|
|
33
|
+
let c = coords;
|
|
34
|
+
while (Array.isArray(c) && Array.isArray(c[0])) c = c[0];
|
|
35
|
+
if (Array.isArray(c) && typeof c[0] === "number" && typeof c[1] === "number") {
|
|
36
|
+
return [c[0], c[1]];
|
|
37
|
+
}
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
function mapToDegradedTable(params) {
|
|
41
|
+
var _a, _b, _c, _d;
|
|
42
|
+
const rows = [];
|
|
43
|
+
for (const m of params.markers ?? []) {
|
|
44
|
+
const lat = Array.isArray(m.position) ? m.position[0] : (_a = m.position) == null ? void 0 : _a.lat;
|
|
45
|
+
const lng = Array.isArray(m.position) ? m.position[1] : (_b = m.position) == null ? void 0 : _b.lng;
|
|
46
|
+
rows.push(["marker", cell(lat), cell(lng), cell(m.tooltip ?? m.popup ?? "")]);
|
|
47
|
+
}
|
|
48
|
+
const fc = params.geojson;
|
|
49
|
+
const features = Array.isArray(fc == null ? void 0 : fc.features) ? fc.features : [];
|
|
50
|
+
for (const f of features) {
|
|
51
|
+
const ll = firstLngLat((_c = f.geometry) == null ? void 0 : _c.coordinates);
|
|
52
|
+
const props = f.properties ?? {};
|
|
53
|
+
const propSummary = Object.keys(props).slice(0, 3).map((k) => `${k}=${cell(props[k])}`).join(", ");
|
|
54
|
+
rows.push([
|
|
55
|
+
cell(((_d = f.geometry) == null ? void 0 : _d.type) ?? "feature"),
|
|
56
|
+
ll ? cell(ll[1]) : "",
|
|
57
|
+
ll ? cell(ll[0]) : "",
|
|
58
|
+
propSummary
|
|
59
|
+
]);
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
columns: ["Type", "Lat", "Lng", "Info"],
|
|
63
|
+
rows: rows.slice(0, MAX_PROJECTED_ROWS)
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
function chartToDegradedTable(params) {
|
|
67
|
+
var _a, _b;
|
|
68
|
+
const datasets = ((_a = params.data) == null ? void 0 : _a.datasets) ?? [];
|
|
69
|
+
const labels = ((_b = params.data) == null ? void 0 : _b.labels) ?? [];
|
|
70
|
+
const rowCount = Math.max(labels.length, ...datasets.map((d) => {
|
|
71
|
+
var _a2;
|
|
72
|
+
return ((_a2 = d.data) == null ? void 0 : _a2.length) ?? 0;
|
|
73
|
+
}), 0);
|
|
74
|
+
const columns = ["", ...datasets.map((d, i) => d.label ?? `Series ${i + 1}`)];
|
|
75
|
+
const rows = [];
|
|
76
|
+
for (let r = 0; r < Math.min(rowCount, MAX_PROJECTED_ROWS); r++) {
|
|
77
|
+
rows.push([cell(labels[r] ?? r + 1), ...datasets.map((d) => {
|
|
78
|
+
var _a2;
|
|
79
|
+
return cell((_a2 = d.data) == null ? void 0 : _a2[r]);
|
|
80
|
+
})]);
|
|
81
|
+
}
|
|
82
|
+
return { columns, rows };
|
|
83
|
+
}
|
|
84
|
+
exports.chartToDegradedTable = chartToDegradedTable;
|
|
85
|
+
exports.graphToDegradedTable = graphToDegradedTable;
|
|
86
|
+
exports.mapToDegradedTable = mapToDegradedTable;
|
|
87
|
+
//# sourceMappingURL=degraded-projections.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"degraded-projections.cjs","sources":["../../src/utils/degraded-projections.ts"],"sourcesContent":["/**\n * Degraded-fallback projections (audit 2026-05-30, P2.5).\n *\n * Pure functions that turn a heavy renderer's params into a flat\n * `{ columns, rows }` table for `<DegradedFallback>` — the middle rung of\n * the fallback ladder shown when the native render (G6 / Leaflet / Chart.js)\n * is unavailable or throws. No peer deps, no side effects, fully testable.\n *\n * These are best-effort views: they surface the underlying data so the user\n * isn't left with a blank space, not faithful reproductions of the chart/\n * map/graph.\n */\n\nexport interface DegradedTable {\n columns: string[];\n rows: Array<Array<string | number>>;\n}\n\nconst MAX_PROJECTED_ROWS = 200;\n\n/** Compact a value to a single table cell string. */\nfunction cell(value: unknown): string {\n if (value == null) return '';\n if (typeof value === 'object') {\n try {\n return JSON.stringify(value);\n } catch {\n return String(value);\n }\n }\n return String(value);\n}\n\n// ─── Graph ───────────────────────────────────────────────────────────────\n\n/**\n * Graph → edge table when edges exist (Source / Target / Label), else a\n * node list (Node / Label). The graph renderer can therefore degrade to a\n * readable relationship listing instead of a blank canvas.\n */\nexport function graphToDegradedTable(params: {\n nodes?: Array<{ id: string; label?: string }>;\n edges?: Array<{ source: string; target: string; label?: string; weight?: number }>;\n}): DegradedTable {\n const edges = params.edges ?? [];\n if (edges.length > 0) {\n return {\n columns: ['Source', 'Target', 'Label'],\n rows: edges.slice(0, MAX_PROJECTED_ROWS).map((e) => {\n const label = [e.weight != null ? String(e.weight) : '', e.label ?? '']\n .filter(Boolean)\n .join(' · ');\n return [cell(e.source), cell(e.target), label];\n }),\n };\n }\n const nodes = params.nodes ?? [];\n return {\n columns: ['Node', 'Label'],\n rows: nodes.slice(0, MAX_PROJECTED_ROWS).map((n) => [cell(n.id), cell(n.label ?? n.id)]),\n };\n}\n\n// ─── Map ───────────────────────────────────────────────────────────────────\n\ninterface GeoJSONLikeFeature {\n geometry?: { type?: string; coordinates?: unknown } | null;\n properties?: Record<string, unknown> | null;\n}\n\n/** Pull a representative `[lng, lat]` pair out of a geometry's coordinates. */\nfunction firstLngLat(coords: unknown): [number, number] | null {\n let c: unknown = coords;\n // Descend nested arrays until we reach a [number, number, ...] position.\n while (Array.isArray(c) && Array.isArray(c[0])) c = c[0];\n if (Array.isArray(c) && typeof c[0] === 'number' && typeof c[1] === 'number') {\n return [c[0], c[1]];\n }\n return null;\n}\n\n/**\n * Map → a coordinate table. Markers become Lat/Lng/Label rows; GeoJSON\n * features become Type / Lat / Lng (+ a compact properties summary). So a\n * map that can't paint still lists where its points are.\n */\nexport function mapToDegradedTable(params: {\n markers?: Array<{\n position: [number, number] | { lat: number; lng: number };\n tooltip?: string;\n popup?: string;\n }>;\n geojson?: unknown;\n}): DegradedTable {\n const rows: Array<Array<string | number>> = [];\n\n for (const m of params.markers ?? []) {\n const lat = Array.isArray(m.position) ? m.position[0] : m.position?.lat;\n const lng = Array.isArray(m.position) ? m.position[1] : m.position?.lng;\n rows.push(['marker', cell(lat), cell(lng), cell(m.tooltip ?? m.popup ?? '')]);\n }\n\n const fc = params.geojson as { features?: GeoJSONLikeFeature[] } | undefined;\n const features = Array.isArray(fc?.features) ? fc!.features : [];\n for (const f of features) {\n const ll = firstLngLat(f.geometry?.coordinates);\n const props = f.properties ?? {};\n const propSummary = Object.keys(props)\n .slice(0, 3)\n .map((k) => `${k}=${cell(props[k])}`)\n .join(', ');\n rows.push([\n cell(f.geometry?.type ?? 'feature'),\n ll ? cell(ll[1]) : '',\n ll ? cell(ll[0]) : '',\n propSummary,\n ]);\n }\n\n return {\n columns: ['Type', 'Lat', 'Lng', 'Info'],\n rows: rows.slice(0, MAX_PROJECTED_ROWS),\n };\n}\n\n// ─── Chart ───────────────────────────────────────────────────────────────\n\n/**\n * Chart → a series table: one row per label, one column per dataset. So a\n * chart that can't draw still shows its numbers. Point/object data (scatter,\n * bubble, time series) is stringified per cell.\n */\nexport function chartToDegradedTable(params: {\n data?: {\n labels?: Array<string | number>;\n datasets?: Array<{ label?: string; data?: unknown[] }>;\n };\n}): DegradedTable {\n const datasets = params.data?.datasets ?? [];\n const labels = params.data?.labels ?? [];\n const rowCount = Math.max(labels.length, ...datasets.map((d) => d.data?.length ?? 0), 0);\n\n const columns = ['', ...datasets.map((d, i) => d.label ?? `Series ${i + 1}`)];\n const rows: Array<Array<string | number>> = [];\n for (let r = 0; r < Math.min(rowCount, MAX_PROJECTED_ROWS); r++) {\n rows.push([cell(labels[r] ?? r + 1), ...datasets.map((d) => cell(d.data?.[r]))]);\n }\n return { columns, rows };\n}\n"],"names":["_a"],"mappings":"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"}
|