@seed-ship/mcp-ui-solid 3.0.5 → 4.0.1

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.
Files changed (126) hide show
  1. package/CHANGELOG.md +115 -0
  2. package/README.md +253 -280
  3. package/dist/components/ChartJSRenderer.cjs +37 -15
  4. package/dist/components/ChartJSRenderer.cjs.map +1 -1
  5. package/dist/components/ChartJSRenderer.d.ts.map +1 -1
  6. package/dist/components/ChartJSRenderer.js +37 -15
  7. package/dist/components/ChartJSRenderer.js.map +1 -1
  8. package/dist/components/DataPreviewSection.cjs +213 -0
  9. package/dist/components/DataPreviewSection.cjs.map +1 -0
  10. package/dist/components/DataPreviewSection.d.ts +19 -0
  11. package/dist/components/DataPreviewSection.d.ts.map +1 -0
  12. package/dist/components/DataPreviewSection.js +213 -0
  13. package/dist/components/DataPreviewSection.js.map +1 -0
  14. package/dist/components/MapRenderer.cjs +168 -26
  15. package/dist/components/MapRenderer.cjs.map +1 -1
  16. package/dist/components/MapRenderer.d.ts +2 -2
  17. package/dist/components/MapRenderer.d.ts.map +1 -1
  18. package/dist/components/MapRenderer.js +169 -27
  19. package/dist/components/MapRenderer.js.map +1 -1
  20. package/dist/components/ScratchpadPanel.cjs +83 -1
  21. package/dist/components/ScratchpadPanel.cjs.map +1 -1
  22. package/dist/components/ScratchpadPanel.d.ts.map +1 -1
  23. package/dist/components/ScratchpadPanel.js +84 -2
  24. package/dist/components/ScratchpadPanel.js.map +1 -1
  25. package/dist/components/VerifiedText.cjs +166 -0
  26. package/dist/components/VerifiedText.cjs.map +1 -0
  27. package/dist/components/VerifiedText.d.ts +22 -0
  28. package/dist/components/VerifiedText.d.ts.map +1 -0
  29. package/dist/components/VerifiedText.js +166 -0
  30. package/dist/components/VerifiedText.js.map +1 -0
  31. package/dist/components/index.d.ts +4 -0
  32. package/dist/components/index.d.ts.map +1 -1
  33. package/dist/components.cjs +4 -0
  34. package/dist/components.cjs.map +1 -1
  35. package/dist/components.d.cts +4 -0
  36. package/dist/components.d.ts +4 -0
  37. package/dist/components.js +4 -0
  38. package/dist/components.js.map +1 -1
  39. package/dist/hooks/index.d.ts +2 -0
  40. package/dist/hooks/index.d.ts.map +1 -1
  41. package/dist/hooks/useDataValidator.cjs +31 -0
  42. package/dist/hooks/useDataValidator.cjs.map +1 -0
  43. package/dist/hooks/useDataValidator.d.ts +42 -0
  44. package/dist/hooks/useDataValidator.d.ts.map +1 -0
  45. package/dist/hooks/useDataValidator.js +31 -0
  46. package/dist/hooks/useDataValidator.js.map +1 -0
  47. package/dist/hooks.cjs +2 -0
  48. package/dist/hooks.cjs.map +1 -1
  49. package/dist/hooks.d.cts +2 -0
  50. package/dist/hooks.d.ts +2 -0
  51. package/dist/hooks.js +2 -0
  52. package/dist/hooks.js.map +1 -1
  53. package/dist/index.cjs +8 -0
  54. package/dist/index.cjs.map +1 -1
  55. package/dist/index.d.cts +9 -5
  56. package/dist/index.d.ts +9 -5
  57. package/dist/index.d.ts.map +1 -1
  58. package/dist/index.js +8 -0
  59. package/dist/index.js.map +1 -1
  60. package/dist/node_modules/.pnpm/@mapbox_point-geometry@1.1.0/node_modules/@mapbox/point-geometry/index.cjs +290 -0
  61. package/dist/node_modules/.pnpm/@mapbox_point-geometry@1.1.0/node_modules/@mapbox/point-geometry/index.cjs.map +1 -0
  62. package/dist/node_modules/.pnpm/@mapbox_point-geometry@1.1.0/node_modules/@mapbox/point-geometry/index.js +291 -0
  63. package/dist/node_modules/.pnpm/@mapbox_point-geometry@1.1.0/node_modules/@mapbox/point-geometry/index.js.map +1 -0
  64. package/dist/node_modules/.pnpm/@mapbox_vector-tile@2.0.4/node_modules/@mapbox/vector-tile/index.cjs +243 -0
  65. package/dist/node_modules/.pnpm/@mapbox_vector-tile@2.0.4/node_modules/@mapbox/vector-tile/index.cjs.map +1 -0
  66. package/dist/node_modules/.pnpm/@mapbox_vector-tile@2.0.4/node_modules/@mapbox/vector-tile/index.js +243 -0
  67. package/dist/node_modules/.pnpm/@mapbox_vector-tile@2.0.4/node_modules/@mapbox/vector-tile/index.js.map +1 -0
  68. package/dist/node_modules/.pnpm/color2k@2.0.3/node_modules/color2k/dist/index.exports.import.es.cjs +137 -0
  69. package/dist/node_modules/.pnpm/color2k@2.0.3/node_modules/color2k/dist/index.exports.import.es.cjs.map +1 -0
  70. package/dist/node_modules/.pnpm/color2k@2.0.3/node_modules/color2k/dist/index.exports.import.es.js +137 -0
  71. package/dist/node_modules/.pnpm/color2k@2.0.3/node_modules/color2k/dist/index.exports.import.es.js.map +1 -0
  72. package/dist/node_modules/.pnpm/pbf@4.0.1/node_modules/pbf/index.cjs +686 -0
  73. package/dist/node_modules/.pnpm/pbf@4.0.1/node_modules/pbf/index.cjs.map +1 -0
  74. package/dist/node_modules/.pnpm/pbf@4.0.1/node_modules/pbf/index.js +687 -0
  75. package/dist/node_modules/.pnpm/pbf@4.0.1/node_modules/pbf/index.js.map +1 -0
  76. package/dist/node_modules/.pnpm/pmtiles@3.2.1/node_modules/pmtiles/dist/index.cjs +1366 -0
  77. package/dist/node_modules/.pnpm/pmtiles@3.2.1/node_modules/pmtiles/dist/index.cjs.map +1 -0
  78. package/dist/node_modules/.pnpm/pmtiles@3.2.1/node_modules/pmtiles/dist/index.js +1366 -0
  79. package/dist/node_modules/.pnpm/pmtiles@3.2.1/node_modules/pmtiles/dist/index.js.map +1 -0
  80. package/dist/node_modules/.pnpm/potpack@1.0.2/node_modules/potpack/index.cjs +54 -0
  81. package/dist/node_modules/.pnpm/potpack@1.0.2/node_modules/potpack/index.cjs.map +1 -0
  82. package/dist/node_modules/.pnpm/potpack@1.0.2/node_modules/potpack/index.js +55 -0
  83. package/dist/node_modules/.pnpm/potpack@1.0.2/node_modules/potpack/index.js.map +1 -0
  84. package/dist/node_modules/.pnpm/protomaps-leaflet@4.1.1/node_modules/protomaps-leaflet/dist/esm/index.cjs +1256 -0
  85. package/dist/node_modules/.pnpm/protomaps-leaflet@4.1.1/node_modules/protomaps-leaflet/dist/esm/index.cjs.map +1 -0
  86. package/dist/node_modules/.pnpm/protomaps-leaflet@4.1.1/node_modules/protomaps-leaflet/dist/esm/index.js +1256 -0
  87. package/dist/node_modules/.pnpm/protomaps-leaflet@4.1.1/node_modules/protomaps-leaflet/dist/esm/index.js.map +1 -0
  88. package/dist/node_modules/.pnpm/quickselect@2.0.0/node_modules/quickselect/index.cjs +47 -0
  89. package/dist/node_modules/.pnpm/quickselect@2.0.0/node_modules/quickselect/index.cjs.map +1 -0
  90. package/dist/node_modules/.pnpm/quickselect@2.0.0/node_modules/quickselect/index.js +48 -0
  91. package/dist/node_modules/.pnpm/quickselect@2.0.0/node_modules/quickselect/index.js.map +1 -0
  92. package/dist/node_modules/.pnpm/rbush@3.0.1/node_modules/rbush/index.cjs +378 -0
  93. package/dist/node_modules/.pnpm/rbush@3.0.1/node_modules/rbush/index.cjs.map +1 -0
  94. package/dist/node_modules/.pnpm/rbush@3.0.1/node_modules/rbush/index.js +379 -0
  95. package/dist/node_modules/.pnpm/rbush@3.0.1/node_modules/rbush/index.js.map +1 -0
  96. package/dist/services/data-validator.cjs +85 -0
  97. package/dist/services/data-validator.cjs.map +1 -0
  98. package/dist/services/data-validator.d.ts +28 -0
  99. package/dist/services/data-validator.d.ts.map +1 -0
  100. package/dist/services/data-validator.js +85 -0
  101. package/dist/services/data-validator.js.map +1 -0
  102. package/dist/services/index.d.ts +1 -0
  103. package/dist/services/index.d.ts.map +1 -1
  104. package/dist/types/chat-bus.d.ts +88 -1
  105. package/dist/types/chat-bus.d.ts.map +1 -1
  106. package/dist/types/index.d.ts +135 -6
  107. package/dist/types/index.d.ts.map +1 -1
  108. package/dist/types.d.cts +135 -6
  109. package/dist/types.d.ts +135 -6
  110. package/package.json +5 -1
  111. package/src/components/ChartJSRenderer.tsx +35 -13
  112. package/src/components/DataPreviewSection.tsx +251 -0
  113. package/src/components/MapRenderer.test.tsx +94 -5
  114. package/src/components/MapRenderer.tsx +246 -45
  115. package/src/components/ScratchpadPanel.tsx +19 -3
  116. package/src/components/VerifiedText.tsx +187 -0
  117. package/src/components/index.ts +7 -0
  118. package/src/hooks/index.ts +7 -0
  119. package/src/hooks/useDataValidator.ts +68 -0
  120. package/src/index.ts +26 -1
  121. package/src/services/data-validator.test.ts +151 -0
  122. package/src/services/data-validator.ts +149 -0
  123. package/src/services/index.ts +2 -0
  124. package/src/types/chat-bus.ts +98 -1
  125. package/src/types/index.ts +145 -6
  126. package/tsconfig.tsbuildinfo +1 -1
@@ -1,8 +1,96 @@
1
- import { isServer, getNextElement, template, getNextMarker, insert, createComponent, use, effect, setStyleProperty } from "solid-js/web";
1
+ import { isServer, getNextElement, template, getNextMarker, insert, createComponent, use, effect, setStyleProperty, className } from "solid-js/web";
2
2
  import { createSignal, createEffect, onCleanup, Show } from "solid-js";
3
- var _tmpl$ = /* @__PURE__ */ template(`<div class="p-4 text-red-500 bg-red-50 dark:bg-red-900/20 text-center">`), _tmpl$2 = /* @__PURE__ */ template(`<div class="relative z-0"style=width:100%;z-index:0>`), _tmpl$3 = /* @__PURE__ */ template(`<div class="w-full bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden"><!$><!/><!$><!/>`);
3
+ var _tmpl$ = /* @__PURE__ */ template(`<div class="p-4 text-red-500 bg-red-50 dark:bg-red-900/20 text-center">`), _tmpl$2 = /* @__PURE__ */ template(`<div class="relative z-0"style=width:100%;z-index:0>`), _tmpl$3 = /* @__PURE__ */ template(`<div><!$><!/><!$><!/>`);
4
4
  let L = null;
5
5
  let clusterCssLoaded = false;
6
+ function getChoroplethColor(value, scale, fallback) {
7
+ if (value == null || typeof value !== "number" || !isFinite(value)) return fallback;
8
+ if (scale.length === 0) return fallback;
9
+ if (value <= scale[0][0]) return scale[0][1];
10
+ if (value >= scale[scale.length - 1][0]) return scale[scale.length - 1][1];
11
+ for (let i = 1; i < scale.length; i++) {
12
+ if (value <= scale[i][0]) return scale[i][1];
13
+ }
14
+ return scale[scale.length - 1][1];
15
+ }
16
+ function buildStyleFn(style) {
17
+ if (!style) {
18
+ return () => ({
19
+ fillColor: "#3388ff",
20
+ fillOpacity: 0.6,
21
+ color: "#333",
22
+ weight: 1,
23
+ opacity: 1
24
+ });
25
+ }
26
+ return (feature) => {
27
+ let fillColor = style.fillColor || "#3388ff";
28
+ if (style.choroplethField && style.choroplethScale && (feature == null ? void 0 : feature.properties)) {
29
+ const val = feature.properties[style.choroplethField];
30
+ fillColor = getChoroplethColor(val, style.choroplethScale, style.choroplethFallback || "#ccc");
31
+ }
32
+ return {
33
+ fillColor,
34
+ fillOpacity: style.fillOpacity ?? 0.6,
35
+ color: style.strokeColor || "#333",
36
+ weight: style.strokeWeight ?? 1,
37
+ opacity: style.strokeOpacity ?? 1
38
+ };
39
+ };
40
+ }
41
+ function buildPopupContent(feature, popup) {
42
+ if (!popup || !(feature == null ? void 0 : feature.properties)) return null;
43
+ const props = feature.properties;
44
+ if (popup.template) {
45
+ return popup.template.replace(/\{\{(\w+)\}\}/g, (_, key) => {
46
+ const val = props[key];
47
+ return val != null ? String(val) : "";
48
+ });
49
+ }
50
+ const parts = [];
51
+ if (popup.titleField && props[popup.titleField] != null) {
52
+ parts.push(`<strong>${escapeHtml(String(props[popup.titleField]))}</strong>`);
53
+ }
54
+ const fields = popup.fields || Object.keys(props).slice(0, 8);
55
+ for (const key of fields) {
56
+ if (key === popup.titleField) continue;
57
+ const val = props[key];
58
+ if (val == null) continue;
59
+ const formatted = typeof val === "number" ? val.toLocaleString("fr-FR") : String(val);
60
+ parts.push(`<span style="color:#666;font-size:11px">${escapeHtml(key)}</span>: ${escapeHtml(formatted)}`);
61
+ }
62
+ return parts.join("<br/>");
63
+ }
64
+ function escapeHtml(str) {
65
+ return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
66
+ }
67
+ function addGeoJSONLayer(mapInst, leaflet, geojson, style, popup) {
68
+ const styleFn = buildStyleFn(style);
69
+ const layer = leaflet.geoJSON(geojson, {
70
+ style: styleFn,
71
+ pointToLayer: (feature, latlng) => {
72
+ const s = styleFn(feature);
73
+ return leaflet.circleMarker(latlng, {
74
+ radius: 6,
75
+ fillColor: s.fillColor,
76
+ fillOpacity: s.fillOpacity,
77
+ color: s.color,
78
+ weight: s.weight,
79
+ opacity: s.opacity
80
+ });
81
+ },
82
+ onEachFeature: (feature, featureLayer) => {
83
+ const html = buildPopupContent(feature, popup);
84
+ if (html) {
85
+ featureLayer.bindPopup(html, {
86
+ maxWidth: 300
87
+ });
88
+ }
89
+ }
90
+ });
91
+ layer.addTo(mapInst);
92
+ return layer;
93
+ }
6
94
  const MapRenderer = (props) => {
7
95
  let mapContainer;
8
96
  let mapInstance = null;
@@ -13,7 +101,7 @@ const MapRenderer = (props) => {
13
101
  return props.params || ((_a = props.component) == null ? void 0 : _a.params);
14
102
  };
15
103
  createEffect(async () => {
16
- var _a, _b, _c;
104
+ var _a, _b, _c, _d, _e;
17
105
  if (isServer) return;
18
106
  if (!L) {
19
107
  try {
@@ -56,8 +144,9 @@ const MapRenderer = (props) => {
56
144
  }
57
145
  if (mapInstance && L) {
58
146
  const p = params();
147
+ const allBoundsLayers = [];
59
148
  mapInstance.eachLayer((layer) => {
60
- if (layer instanceof L.Marker || layer._group || layer._featureGroup) {
149
+ if (layer instanceof L.Marker || layer instanceof L.GeoJSON || layer instanceof L.CircleMarker || layer._group || layer._featureGroup) {
61
150
  mapInstance.removeLayer(layer);
62
151
  }
63
152
  });
@@ -81,44 +170,93 @@ const MapRenderer = (props) => {
81
170
  });
82
171
  (_a = p == null ? void 0 : p.markers) == null ? void 0 : _a.forEach((marker) => {
83
172
  const m = L.marker(marker.position);
84
- if (marker.tooltip) {
85
- m.bindTooltip(marker.tooltip);
86
- }
87
- if (marker.popup) {
88
- m.bindPopup(marker.popup);
89
- }
173
+ if (marker.tooltip) m.bindTooltip(marker.tooltip);
174
+ if (marker.popup) m.bindPopup(marker.popup);
90
175
  clusterGroup.addLayer(m);
91
176
  markers.push(m);
92
177
  });
93
178
  mapInstance.addLayer(clusterGroup);
94
- } catch (e) {
95
- console.warn("Failed to load leaflet.markercluster, falling back to regular markers", e);
179
+ } catch {
96
180
  (_b = p == null ? void 0 : p.markers) == null ? void 0 : _b.forEach((marker) => {
97
181
  const m = L.marker(marker.position).addTo(mapInstance);
98
- if (marker.tooltip) {
99
- m.bindTooltip(marker.tooltip);
100
- }
101
- if (marker.popup) {
102
- m.bindPopup(marker.popup);
103
- }
182
+ if (marker.tooltip) m.bindTooltip(marker.tooltip);
183
+ if (marker.popup) m.bindPopup(marker.popup);
104
184
  markers.push(m);
105
185
  });
106
186
  }
107
187
  } else {
108
188
  (_c = p == null ? void 0 : p.markers) == null ? void 0 : _c.forEach((marker) => {
109
189
  const m = L.marker(marker.position).addTo(mapInstance);
110
- if (marker.tooltip) {
111
- m.bindTooltip(marker.tooltip);
112
- }
113
- if (marker.popup) {
114
- m.bindPopup(marker.popup);
115
- }
190
+ if (marker.tooltip) m.bindTooltip(marker.tooltip);
191
+ if (marker.popup) m.bindPopup(marker.popup);
116
192
  markers.push(m);
117
193
  });
118
194
  }
119
- if ((p == null ? void 0 : p.fitBounds) && markers.length > 0) {
120
- const group = L.featureGroup(markers);
121
- mapInstance.fitBounds(group.getBounds().pad(0.1));
195
+ if (markers.length > 0) {
196
+ allBoundsLayers.push(...markers);
197
+ }
198
+ if (p == null ? void 0 : p.geojson) {
199
+ const geoLayer = addGeoJSONLayer(mapInstance, L, p.geojson, p.geojsonStyle, p.popup);
200
+ allBoundsLayers.push(geoLayer);
201
+ }
202
+ if ((p == null ? void 0 : p.layers) && p.layers.length > 0) {
203
+ const overlays = {};
204
+ for (const layerDef of p.layers) {
205
+ const geoLayer = addGeoJSONLayer(mapInstance, L, layerDef.geojson, layerDef.style || (p == null ? void 0 : p.geojsonStyle), layerDef.popup || (p == null ? void 0 : p.popup));
206
+ overlays[layerDef.name] = geoLayer;
207
+ allBoundsLayers.push(geoLayer);
208
+ if (layerDef.visible === false) {
209
+ mapInstance.removeLayer(geoLayer);
210
+ }
211
+ }
212
+ if (Object.keys(overlays).length > 1) {
213
+ L.control.layers(null, overlays, {
214
+ collapsed: true
215
+ }).addTo(mapInstance);
216
+ }
217
+ }
218
+ if (p == null ? void 0 : p.pmtiles) {
219
+ try {
220
+ const protomaps = await import(
221
+ /* @vite-ignore */
222
+ "../node_modules/.pnpm/protomaps-leaflet@4.1.1/node_modules/protomaps-leaflet/dist/esm/index.js"
223
+ );
224
+ const pmConfig = p.pmtiles;
225
+ const paintRules = ((_d = pmConfig.paintRules) == null ? void 0 : _d.map((rule) => ({
226
+ dataLayer: rule.dataLayer,
227
+ symbolizer: new protomaps[rule.symbolizer === "polygon" ? "PolygonSymbolizer" : rule.symbolizer === "line" ? "LineSymbolizer" : "CircleSymbolizer"]({
228
+ fill: rule.color || "#3388ff",
229
+ stroke: rule.color || "#333",
230
+ width: rule.width ?? 1,
231
+ opacity: rule.opacity ?? 0.6
232
+ })
233
+ }))) || [];
234
+ const labelRules = ((_e = pmConfig.labelRules) == null ? void 0 : _e.map((rule) => ({
235
+ dataLayer: rule.dataLayer,
236
+ symbolizer: new protomaps.TextSymbolizer({
237
+ label_props: [rule.textField],
238
+ fontSize: rule.fontSize ?? 12
239
+ })
240
+ }))) || [];
241
+ const pmLayer = protomaps.leafletLayer({
242
+ url: pmConfig.url,
243
+ attribution: pmConfig.attribution,
244
+ paintRules,
245
+ labelRules,
246
+ maxZoom: pmConfig.maxZoom,
247
+ minZoom: pmConfig.minZoom
248
+ });
249
+ pmLayer.addTo(mapInstance);
250
+ } catch (e) {
251
+ console.warn("[MCP-UI] Failed to load protomaps-leaflet for PMTiles:", e);
252
+ }
253
+ }
254
+ if ((p == null ? void 0 : p.fitBounds) && allBoundsLayers.length > 0) {
255
+ const group = L.featureGroup(allBoundsLayers);
256
+ const bounds = group.getBounds();
257
+ if (bounds.isValid()) {
258
+ mapInstance.fitBounds(bounds.pad(0.1));
259
+ }
122
260
  } else if (p == null ? void 0 : p.center) {
123
261
  mapInstance.setView(p.center, p.zoom || mapInstance.getZoom());
124
262
  }
@@ -157,6 +295,10 @@ const MapRenderer = (props) => {
157
295
  return _el$3;
158
296
  }
159
297
  }), _el$7, _co$2);
298
+ effect(() => {
299
+ var _a;
300
+ return className(_el$, `w-full bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden ${((_a = params()) == null ? void 0 : _a.className) || ""}`);
301
+ });
160
302
  return _el$;
161
303
  })();
162
304
  };
@@ -1 +1 @@
1
- {"version":3,"file":"MapRenderer.js","sources":["../../src/components/MapRenderer.tsx"],"sourcesContent":["/**\n * MapRenderer - Interactive map Component\n * Sprint 6: Code & Maps\n * Sprint Ultimate U.2: Marker Clustering Support\n */\n\nimport { Component, createEffect, onCleanup, createSignal, Show } from 'solid-js'\nimport { isServer } from 'solid-js/web'\nimport type { UIComponent, MapComponentParams, MapClusterOptions } from '../types'\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\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\n const params = () => props.params || (props.component?.params as MapComponentParams)\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 || '&copy; <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 - Sprint Ultimate U.2: Clustering support\n if (mapInstance && L) {\n const p = params()\n\n // Clear existing layers (markers and cluster groups)\n mapInstance.eachLayer((layer: any) => {\n if (layer instanceof L.Marker || layer._group || layer._featureGroup) {\n mapInstance.removeLayer(layer)\n }\n })\n\n const markers: any[] = []\n const shouldCluster = p?.clustering && p?.markers && p.markers.length > 0\n\n if (shouldCluster) {\n // Sprint Ultimate U.2: Use marker clustering\n try {\n // Lazy load markercluster plugin\n // Import markercluster plugin for side-effects (registers with Leaflet)\n await import('leaflet.markercluster')\n\n // Load cluster CSS if not already loaded\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\n // Get cluster options\n const clusterOpts: MapClusterOptions = typeof p.clustering === 'object' ? p.clustering : {}\n\n // Create cluster group with options\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\n // Add markers to cluster group\n p?.markers?.forEach(marker => {\n const m = L.marker(marker.position)\n if (marker.tooltip) {\n m.bindTooltip(marker.tooltip)\n }\n if (marker.popup) {\n m.bindPopup(marker.popup)\n }\n clusterGroup.addLayer(m)\n markers.push(m)\n })\n\n mapInstance.addLayer(clusterGroup)\n } catch (e) {\n console.warn('Failed to load leaflet.markercluster, falling back to regular markers', e)\n // Fallback to regular markers\n p?.markers?.forEach(marker => {\n const m = L.marker(marker.position).addTo(mapInstance)\n if (marker.tooltip) {\n m.bindTooltip(marker.tooltip)\n }\n if (marker.popup) {\n m.bindPopup(marker.popup)\n }\n markers.push(m)\n })\n }\n } else {\n // Standard marker rendering (no clustering)\n p?.markers?.forEach(marker => {\n const m = L.marker(marker.position).addTo(mapInstance)\n if (marker.tooltip) {\n m.bindTooltip(marker.tooltip)\n }\n if (marker.popup) {\n m.bindPopup(marker.popup)\n }\n markers.push(m)\n })\n }\n\n // Handle fitBounds\n if (p?.fitBounds && markers.length > 0) {\n const group = L.featureGroup(markers)\n mapInstance.fitBounds(group.getBounds().pad(0.1))\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 <div class=\"w-full bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden\">\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={{ height: params()?.height || '400px', width: '100%', \"z-index\": 0 }}\n class=\"relative z-0\"\n />\n </Show>\n </div>\n )\n}\n"],"names":["L","clusterCssLoaded","MapRenderer","props","mapContainer","mapInstance","isLeafletLoaded","setIsLeafletLoaded","createSignal","error","setError","params","component","createEffect","isServer","module","default","e","console","warn","p","center","zoom","map","zoomControl","scrollWheelZoom","attributionControl","setView","tileLayerUrl","tileLayer","attribution","addTo","control","prefix","Icon","Default","prototype","_getIconUrl","mergeOptions","iconRetinaUrl","iconUrl","shadowUrl","eachLayer","layer","Marker","_group","_featureGroup","removeLayer","markers","shouldCluster","clustering","length","clusterOpts","clusterGroup","markerClusterGroup","maxClusterRadius","spiderfyOnMaxZoom","showCoverageOnHover","disableClusteringAtZoom","animate","animateAddingMarkers","forEach","marker","m","position","tooltip","bindTooltip","popup","bindPopup","addLayer","push","fitBounds","group","featureGroup","getBounds","pad","getZoom","onCleanup","remove","_el$","_$getNextElement","_tmpl$3","_el$4","firstChild","_el$5","_co$","_$getNextMarker","nextSibling","_el$6","_el$7","_co$2","_$insert","_$createComponent","Show","when","children","_el$2","_tmpl$","_el$3","_tmpl$2","_ref$","_$use","_$effect","_$p","_$setStyleProperty","height"],"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 { UIComponent, MapComponentParams, MapClusterOptions, MapGeoJSONStyle, MapPopupConfig, MapLayer, MapPMTilesConfig } from '../types'\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// ─── 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, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\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\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\n const params = () => props.params || (props.component?.params as MapComponentParams)\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 || '&copy; <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 <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 <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={{ height: params()?.height || '400px', width: '100%', \"z-index\": 0 }}\n class=\"relative z-0\"\n />\n </Show>\n </div>\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","MapRenderer","mapContainer","mapInstance","isLeafletLoaded","setIsLeafletLoaded","createSignal","error","setError","params","component","createEffect","isServer","module","default","e","console","warn","p","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","markers","shouldCluster","clustering","clusterOpts","clusterGroup","markerClusterGroup","maxClusterRadius","spiderfyOnMaxZoom","showCoverageOnHover","disableClusteringAtZoom","animate","animateAddingMarkers","forEach","marker","m","position","tooltip","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","_el$","_$getNextElement","_tmpl$3","_el$4","firstChild","_el$5","_co$","_$getNextMarker","nextSibling","_el$6","_el$7","_co$2","_$insert","_$createComponent","Show","when","children","_el$2","_tmpl$","_el$3","_tmpl$2","_ref$","_$use","_$effect","_$p","_$setStyleProperty","height","_$className","className"],"mappings":"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"}
@@ -3,6 +3,10 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const web = require("solid-js/web");
4
4
  const solidJs = require("solid-js");
5
5
  const FormFieldRenderer = require("./FormFieldRenderer.cjs");
6
+ const VerifiedText = require("./VerifiedText.cjs");
7
+ const DataPreviewSection = require("./DataPreviewSection.cjs");
8
+ const MapRenderer = require("./MapRenderer.cjs");
9
+ const ChartJSRenderer = require("./ChartJSRenderer.cjs");
6
10
  var _tmpl$ = /* @__PURE__ */ web.template(`<svg fill=none viewBox="0 0 24 24"stroke=currentColor><path stroke-linecap=round stroke-linejoin=round stroke-width=2 d="M19 9l-7 7-7-7">`), _tmpl$2 = /* @__PURE__ */ web.template(`<button class="p-1 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors"aria-label=Close><svg class="w-4 h-4"fill=none viewBox="0 0 24 24"stroke=currentColor><path stroke-linecap=round stroke-linejoin=round stroke-width=2 d="M6 18L18 6M6 6l12 12">`), _tmpl$3 = /* @__PURE__ */ web.template(`<div class="px-4 py-2 border-b border-gray-100 dark:border-gray-700 flex flex-wrap items-center gap-1.5">`), _tmpl$4 = /* @__PURE__ */ web.template(`<div class="px-4 py-3 border-t border-gray-100 dark:border-gray-700 space-y-2">`), _tmpl$5 = /* @__PURE__ */ web.template(`<div class="flex flex-col items-center gap-2 py-4 text-center"><span class=text-2xl>&#128269;</span><p class="text-sm text-gray-500 dark:text-gray-400">No results for these filters</p><button type=button class="px-3 py-1.5 text-xs font-medium text-blue-600 dark:text-blue-400 hover:bg-blue-50 dark:hover:bg-blue-900/20 rounded-lg transition-colors">Modify filters`), _tmpl$6 = /* @__PURE__ */ web.template(`<div class="px-4 py-3 border-t border-gray-100 dark:border-gray-700">`), _tmpl$7 = /* @__PURE__ */ web.template(`<p class="text-xs text-red-500 dark:text-red-500 mt-0.5">Code: <!$><!/>`), _tmpl$8 = /* @__PURE__ */ web.template(`<button type=button class="px-3 py-1.5 text-sm font-medium rounded-lg bg-red-600 text-white hover:bg-red-700 transition-colors flex items-center gap-1">&#128260; Retry`), _tmpl$9 = /* @__PURE__ */ web.template(`<button type=button class="px-3 py-1.5 text-sm font-medium rounded-lg border border-gray-200 dark:border-gray-600 text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors">Close`), _tmpl$0 = /* @__PURE__ */ web.template(`<div class="px-4 py-3 border-t border-red-100 dark:border-red-900/30 bg-red-50 dark:bg-red-900/10"><div class="flex items-start gap-2 text-sm text-red-700 dark:text-red-400"><span class="flex-shrink-0 mt-0.5">⚠️</span><div class=flex-1><p class=font-medium></p><!$><!/></div></div><div class="flex gap-2 mt-2"><!$><!/><!$><!/>`), _tmpl$1 = /* @__PURE__ */ web.template(`<div class="px-4 py-3 border-t border-gray-100 dark:border-gray-700"><button type=button class="w-full px-4 py-2.5 text-sm font-semibold rounded-lg text-white bg-blue-600 hover:bg-blue-700 transition-colors flex items-center justify-center gap-2"><svg class="w-4 h-4"fill=none viewBox="0 0 24 24"stroke=currentColor><path stroke-linecap=round stroke-linejoin=round stroke-width=2 d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path></svg>Search`), _tmpl$10 = /* @__PURE__ */ web.template(`<div style=overflow-y:auto><!$><!/><div class="divide-y divide-gray-100 dark:divide-gray-700"></div><!$><!/><!$><!/><!$><!/><!$><!/>`), _tmpl$11 = /* @__PURE__ */ web.template(`<div class="absolute bottom-1 right-1 px-2 py-1 text-[9px] font-mono bg-black/80 text-green-400 rounded z-50 pointer-events-none"><!$><!/> | ev:<!$><!/> | sec:<!$><!/> | <!$><!/> | last:<!$><!/>`), _tmpl$12 = /* @__PURE__ */ web.template(`<div style="animation:scratchpad-slide-down 0.2s ease-out"><div><div class="flex items-center gap-2"><span class=text-base>&#128221;</span><h3 class="text-sm font-semibold text-gray-900 dark:text-white"></h3><!$><!/></div><div class="flex items-center gap-2"><span></span><!$><!/></div></div><!$><!/><style>
7
11
  @keyframes scratchpad-slide-down { from { opacity: 0; transform: translateY(-8px); } to { opacity: 1; transform: translateY(0); } }
8
12
  </style><!$><!/>`), _tmpl$13 = /* @__PURE__ */ web.template(`<svg class="w-3 h-3 text-gray-300 dark:text-gray-600 flex-shrink-0"fill=none viewBox="0 0 24 24"stroke=currentColor><path stroke-linecap=round stroke-linejoin=round stroke-width=2 d="M9 5l7 7-7 7">`), _tmpl$14 = /* @__PURE__ */ web.template(`<span class="ml-1 font-normal opacity-75">— <!$><!/>`), _tmpl$15 = /* @__PURE__ */ web.template(`<span><!$><!/> <!$><!/><!$><!/>`), _tmpl$16 = /* @__PURE__ */ web.template(`<div><span class="flex-shrink-0 mt-0.5"></span><p>`), _tmpl$17 = /* @__PURE__ */ web.template(`<div class="flex items-center gap-2 mb-2"><span class="text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wide">Preview</span><span class="px-1.5 py-0.5 text-xs font-bold bg-blue-100 dark:bg-blue-900/30 text-blue-700 dark:text-blue-300 rounded">`), _tmpl$18 = /* @__PURE__ */ web.template(`<p class="text-sm text-gray-700 dark:text-gray-300">`), _tmpl$19 = /* @__PURE__ */ web.template(`<div class="mt-2 overflow-x-auto"><table class="min-w-full text-xs"><thead><tr></tr></thead><tbody>`), _tmpl$20 = /* @__PURE__ */ web.template(`<th class="px-2 py-1 text-left font-medium text-gray-500 dark:text-gray-400">`), _tmpl$21 = /* @__PURE__ */ web.template(`<tr class="border-t border-gray-100 dark:border-gray-700">`), _tmpl$22 = /* @__PURE__ */ web.template(`<td class="px-2 py-1 text-gray-700 dark:text-gray-300">`), _tmpl$23 = /* @__PURE__ */ web.template(`<pre class="text-xs text-gray-500 overflow-auto">`), _tmpl$24 = /* @__PURE__ */ web.template(`<div class="px-4 py-3"><h4 class="text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wide mb-2"></h4><!$><!/>`), _tmpl$25 = /* @__PURE__ */ web.template(`<div class=space-y-1>`), _tmpl$26 = /* @__PURE__ */ web.template(`<div class="flex gap-2 text-xs"><span class="text-gray-500 dark:text-gray-400 font-mono min-w-[120px]"><!$><!/>:</span><span class="text-gray-900 dark:text-white">`), _tmpl$27 = /* @__PURE__ */ web.template(`<p class="text-xs text-gray-400 italic">No filters`), _tmpl$28 = /* @__PURE__ */ web.template(`<div class="flex flex-wrap gap-1.5"><!$><!/><!$><!/>`), _tmpl$29 = /* @__PURE__ */ web.template(`<button type=button class="ml-0.5 hover:text-blue-900 dark:hover:text-blue-100">&times;`), _tmpl$30 = /* @__PURE__ */ web.template(`<span class="inline-flex items-center gap-1 px-2.5 py-1 text-xs font-medium bg-blue-100 dark:bg-blue-900/30 text-blue-700 dark:text-blue-300 rounded-full"><button type=button class=hover:underline><span class="text-blue-500 dark:text-blue-400"><!$><!/>:</span> <!$><!/></button><!$><!/>`), _tmpl$31 = /* @__PURE__ */ web.template(`<div class="max-h-48 overflow-y-auto">`), _tmpl$32 = /* @__PURE__ */ web.template(`<button type=button class="mt-1 w-full px-2 py-1 text-xs text-gray-500 hover:text-gray-700 text-center">Cancel`), _tmpl$33 = /* @__PURE__ */ web.template(`<div class="absolute z-50 mt-1 left-0 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-600 rounded-lg shadow-lg p-2 min-w-[200px]">`), _tmpl$34 = /* @__PURE__ */ web.template(`<div class=relative><!$><!/><!$><!/>`), _tmpl$35 = /* @__PURE__ */ web.template(`<button type=button class="inline-flex items-center gap-1 px-2.5 py-1 text-xs font-medium border border-dashed border-gray-300 dark:border-gray-600 text-gray-500 dark:text-gray-400 rounded-full hover:border-blue-400 hover:text-blue-500 transition-colors">+ <!$><!/>`), _tmpl$36 = /* @__PURE__ */ web.template(`<form class="flex gap-1"><input type=text autofocus class="flex-1 px-2 py-1 text-sm border border-gray-200 dark:border-gray-600 rounded bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:border-blue-400 outline-none"><button type=submit class="px-2 py-1 text-xs bg-blue-600 text-white rounded hover:bg-blue-700">OK</button><button type=button class="px-2 py-1 text-xs text-gray-500 hover:text-gray-700">Cancel`), _tmpl$37 = /* @__PURE__ */ web.template(`<span class=ml-1>✓`), _tmpl$38 = /* @__PURE__ */ web.template(`<button type=button><!$><!/><!$><!/>`), _tmpl$39 = /* @__PURE__ */ web.template(`<form class="flex flex-col gap-3"><!$><!/><div class="flex justify-end"><button type=submit class="px-4 py-2 text-sm font-medium rounded-lg text-white bg-blue-600 hover:bg-blue-700 transition-colors">`), _tmpl$40 = /* @__PURE__ */ web.template(`<div class="flex justify-end"><button type=button class="px-3 py-1.5 text-sm font-medium text-blue-600 dark:text-blue-400 hover:bg-blue-50 dark:hover:bg-blue-900/20 rounded-lg transition-colors flex items-center gap-1">Next <svg class="w-3.5 h-3.5"fill=none viewBox="0 0 24 24"stroke=currentColor><path stroke-linecap=round stroke-linejoin=round stroke-width=2 d="M9 5l7 7-7 7">`), _tmpl$41 = /* @__PURE__ */ web.template(`<div class=space-y-3><!$><!/><!$><!/>`), _tmpl$42 = /* @__PURE__ */ web.template(`<span class="text-xs font-normal text-gray-500 dark:text-gray-400">— <!$><!/>`), _tmpl$43 = /* @__PURE__ */ web.template(`<div class="mt-2 ml-6">`), _tmpl$44 = /* @__PURE__ */ web.template(`<div><div><span></span><!$><!/><!$><!/></div><!$><!/>`), _tmpl$45 = /* @__PURE__ */ web.template(`<div class="flex flex-wrap gap-2">`), _tmpl$46 = /* @__PURE__ */ web.template(`<span class=mr-1>`), _tmpl$47 = /* @__PURE__ */ web.template(`<div class=space-y-2><div class=space-y-1.5></div><!$><!/>`), _tmpl$48 = /* @__PURE__ */ web.template(`<div class="flex items-center gap-2 text-sm"><span></span><span class="text-gray-900 dark:text-white">`), _tmpl$49 = /* @__PURE__ */ web.template(`<div class="flex items-start gap-1.5 text-xs text-amber-600 dark:text-amber-400"><span class=flex-shrink-0>⚠️</span><span>`), _tmpl$50 = /* @__PURE__ */ web.template(`<div class="flex gap-1"><input type=text class="flex-1 px-3 py-2 text-sm border border-gray-200 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:border-blue-400 outline-none"><button type=button class="px-3 py-2 text-sm bg-blue-600 text-white rounded-lg hover:bg-blue-700">Send`), _tmpl$51 = /* @__PURE__ */ web.template(`<div class=space-y-3><p class="text-sm text-gray-700 dark:text-gray-300"></p><div class="flex flex-wrap gap-2"></div><!$><!/>`), _tmpl$52 = /* @__PURE__ */ web.template(`<span>`), _tmpl$53 = /* @__PURE__ */ web.template(`<p class="text-xs text-gray-500 dark:text-gray-400 italic">"<!$><!/>"`), _tmpl$54 = /* @__PURE__ */ web.template(`<div class="mt-2 px-3 py-2 bg-blue-50 dark:bg-blue-900/10 rounded-lg text-sm text-blue-700 dark:text-blue-300"><span class=font-medium>Plan:</span> <!$><!/>`), _tmpl$55 = /* @__PURE__ */ web.template(`<button type=button class="text-xs text-blue-600 dark:text-blue-400 hover:underline flex items-center gap-1">&#9998; Modify`), _tmpl$56 = /* @__PURE__ */ web.template(`<div class=space-y-2><!$><!/><div class=space-y-1></div><!$><!/><!$><!/>`), _tmpl$57 = /* @__PURE__ */ web.template(`<div class="flex gap-2 text-sm"><span class="text-gray-500 dark:text-gray-400 font-medium min-w-[80px]"><!$><!/>:</span><span class="text-gray-900 dark:text-white">`), _tmpl$58 = /* @__PURE__ */ web.template(`<div class="flex items-center gap-1 flex-wrap">`), _tmpl$59 = /* @__PURE__ */ web.template(`<div class=space-y-2>`), _tmpl$60 = /* @__PURE__ */ web.template(`<span class="ml-1 text-xs text-gray-500 dark:text-gray-400">— <!$><!/>`), _tmpl$61 = /* @__PURE__ */ web.template(`<span class="ml-1 text-xs text-gray-400">(<!$><!/>ms)`), _tmpl$62 = /* @__PURE__ */ web.template(`<div><span class=flex-shrink-0></span><div><span></span><!$><!/><!$><!/>`), _tmpl$63 = /* @__PURE__ */ web.template(`<span class="text-[10px] opacity-75">`), _tmpl$64 = /* @__PURE__ */ web.template(`<div><span></span><span></span><!$><!/>`), _tmpl$65 = /* @__PURE__ */ web.template(`<button type=button>&#128260; <!$><!/>`), _tmpl$66 = /* @__PURE__ */ web.template(`<button type=button class="px-2 py-1 text-xs opacity-70 hover:opacity-100">&#9654; Details`), _tmpl$67 = /* @__PURE__ */ web.template(`<pre class="mt-2 text-xs opacity-70 overflow-x-auto">`), _tmpl$68 = /* @__PURE__ */ web.template(`<div><div class="flex items-start gap-2"><span class=flex-shrink-0></span><div class=flex-1><p></p><div class="flex gap-2 mt-2"><!$><!/><!$><!/></div><!$><!/>`), _tmpl$69 = /* @__PURE__ */ web.template(`<span class="text-xs font-bold text-blue-600 dark:text-blue-400"><!$><!/> results`), _tmpl$70 = /* @__PURE__ */ web.template(`<p class="text-[10px] text-gray-400">`), _tmpl$71 = /* @__PURE__ */ web.template(`<div class="rounded-lg border border-gray-200 dark:border-gray-700 px-3 py-2"><div class="flex items-center justify-between mb-1.5"><span class="text-sm font-medium text-gray-900 dark:text-white"><!$><!/> <!$><!/></span><!$><!/></div><div class="flex flex-wrap gap-1.5 mb-1"></div><!$><!/>`), _tmpl$72 = /* @__PURE__ */ web.template(`<span><!$><!/> <!$><!/>`), _tmpl$73 = /* @__PURE__ */ web.template(`<div class=overflow-x-auto><table class="min-w-full text-xs"><thead><tr><th class="px-2 py-1 text-left text-gray-500 dark:text-gray-400"></th><th class="px-2 py-1 text-left font-medium text-blue-600 dark:text-blue-400"></th><th class="px-2 py-1 text-left font-medium text-purple-600 dark:text-purple-400"></th></tr></thead><tbody>`), _tmpl$74 = /* @__PURE__ */ web.template(`<tr><td class="px-2 py-1 font-mono text-gray-500 dark:text-gray-400"></td><td class="px-2 py-1 text-gray-900 dark:text-white"></td><td class="px-2 py-1 text-gray-900 dark:text-white">`);
@@ -613,6 +617,76 @@ const SectionRenderer = (props) => {
613
617
  }
614
618
  });
615
619
  }
620
+ }), web.createComponent(solidJs.Match, {
621
+ get when() {
622
+ return props.section.type === "verified_text";
623
+ },
624
+ get children() {
625
+ return web.createComponent(VerifiedText.VerifiedText, web.mergeProps(() => props.section.content, {
626
+ onHallucinationClick: (h) => {
627
+ var _a;
628
+ return (_a = props.onAction) == null ? void 0 : _a.call(props, "hallucination_click", h);
629
+ }
630
+ }));
631
+ }
632
+ }), web.createComponent(solidJs.Match, {
633
+ get when() {
634
+ return props.section.type === "data_preview";
635
+ },
636
+ get children() {
637
+ return web.createComponent(DataPreviewSection.DataPreviewSection, {
638
+ get content() {
639
+ return props.section.content;
640
+ }
641
+ });
642
+ }
643
+ }), web.createComponent(solidJs.Match, {
644
+ get when() {
645
+ return props.section.type === "map";
646
+ },
647
+ get children() {
648
+ return (() => {
649
+ const c = props.section.content;
650
+ return web.createComponent(MapRenderer.MapRenderer, {
651
+ get params() {
652
+ return {
653
+ geojson: c.geojson,
654
+ center: c.center,
655
+ zoom: c.zoom,
656
+ geojsonStyle: c.style,
657
+ popup: c.popup,
658
+ layers: c.layers,
659
+ height: c.height || "300px",
660
+ fitBounds: true
661
+ };
662
+ }
663
+ });
664
+ })();
665
+ }
666
+ }), web.createComponent(solidJs.Match, {
667
+ get when() {
668
+ return props.section.type === "chart";
669
+ },
670
+ get children() {
671
+ return web.createComponent(ChartJSRenderer.ChartJSRenderer, {
672
+ get component() {
673
+ var _a;
674
+ return {
675
+ id: props.section.id,
676
+ type: "chart",
677
+ position: {
678
+ colStart: 1,
679
+ colSpan: 12
680
+ },
681
+ params: {
682
+ ...props.section.content,
683
+ renderer: "native",
684
+ height: ((_a = props.section.content) == null ? void 0 : _a.height) || "250px"
685
+ }
686
+ };
687
+ }
688
+ });
689
+ }
616
690
  }), web.createComponent(solidJs.Match, {
617
691
  when: true,
618
692
  get children() {
@@ -991,7 +1065,15 @@ const EnrichedStepsSection = (props) => {
991
1065
  })();
992
1066
  };
993
1067
  const ActionSection = (props) => {
994
- const actions = () => Array.isArray(props.content) ? props.content : [];
1068
+ const actions = () => {
1069
+ if (Array.isArray(props.content)) return props.content;
1070
+ const obj = props.content;
1071
+ if (obj && Array.isArray(obj.actions)) {
1072
+ console.warn("[MCP-UI] ActionSection: content should be an array, got { actions: [...] }. Unwrapping automatically.");
1073
+ return obj.actions;
1074
+ }
1075
+ return [];
1076
+ };
995
1077
  return (() => {
996
1078
  var _el$178 = web.getNextElement(_tmpl$45);
997
1079
  web.insert(_el$178, web.createComponent(solidJs.For, {