@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
@@ -0,0 +1,213 @@
1
+ import { delegateEvents, createComponent, getNextElement, template, getNextMarker, insert, runHydrationEvents, effect, setStyleProperty, classList, memo, setProperty } from "solid-js/web";
2
+ import { createMemo, createSignal, Show, For } from "solid-js";
3
+ var _tmpl$ = /* @__PURE__ */ template(`<div class="text-xs text-amber-600 dark:text-amber-400 p-2">[DataPreviewSection] Invalid content format`), _tmpl$2 = /* @__PURE__ */ template(`<span class=font-medium>`), _tmpl$3 = /* @__PURE__ */ template(`<span class="px-1.5 py-0.5 rounded bg-blue-50 dark:bg-blue-900/20 text-blue-700 dark:text-blue-300">`), _tmpl$4 = /* @__PURE__ */ template(`<div class="flex items-center gap-1"><button class="px-2 py-1 text-xs rounded border border-gray-300 dark:border-gray-600 hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors"title="Export CSV">CSV</button><button class="px-2 py-1 text-xs rounded border border-gray-300 dark:border-gray-600 hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors"title="Export JSON">JSON`), _tmpl$5 = /* @__PURE__ */ template(`<div class="flex items-center gap-1"><button class="px-2 py-1 rounded hover:bg-gray-100 dark:hover:bg-gray-700 disabled:opacity-40 transition-colors">&laquo;</button><span><!$><!/> / <!$><!/></span><button class="px-2 py-1 rounded hover:bg-gray-100 dark:hover:bg-gray-700 disabled:opacity-40 transition-colors">&raquo;`), _tmpl$6 = /* @__PURE__ */ template(`<div class=data-preview-section><div class="flex items-center justify-between mb-2"><div class="flex items-center gap-2 text-xs text-gray-500 dark:text-gray-400"><!$><!/><!$><!/></div><!$><!/></div><div class="overflow-x-auto rounded border border-gray-200 dark:border-gray-700"><table class="w-full text-sm"><thead><tr class="bg-gray-50 dark:bg-gray-800"></tr></thead><tbody></tbody></table></div><div class="flex items-center justify-between mt-2 text-xs text-gray-500 dark:text-gray-400"><span></span><!$><!/>`), _tmpl$7 = /* @__PURE__ */ template(`<th class="px-3 py-2 font-medium text-xs text-gray-600 dark:text-gray-400 uppercase tracking-wider border-b border-gray-200 dark:border-gray-700">`), _tmpl$8 = /* @__PURE__ */ template(`<tr class="border-b border-gray-100 dark:border-gray-800 hover:bg-gray-50 dark:hover:bg-gray-800/50 transition-colors">`), _tmpl$9 = /* @__PURE__ */ template(`<td class="px-3 py-2 text-gray-800 dark:text-gray-200">`);
4
+ function formatNumber(value, format) {
5
+ if (typeof value !== "number" || !isFinite(value)) return String(value ?? "");
6
+ if (format === "percent") return `${(value * 100).toFixed(1)}%`;
7
+ if (format === "currency") return `${value.toLocaleString("fr-FR")} EUR`;
8
+ if (Number.isInteger(value)) return value.toLocaleString("fr-FR");
9
+ return value.toLocaleString("fr-FR", {
10
+ maximumFractionDigits: 2
11
+ });
12
+ }
13
+ function formatCell(value, col) {
14
+ if (value == null) return "—";
15
+ if (col.type === "number") return formatNumber(value, col.format);
16
+ if (col.type === "date" && typeof value === "string") {
17
+ try {
18
+ return new Date(value).toLocaleDateString("fr-FR");
19
+ } catch {
20
+ return value;
21
+ }
22
+ }
23
+ return String(value);
24
+ }
25
+ function toCSV(columns, rows) {
26
+ const header = columns.map((c) => `"${c.label.replace(/"/g, '""')}"`).join(";");
27
+ const body = rows.map((row) => columns.map((c) => {
28
+ const val = row[c.key];
29
+ if (val == null) return "";
30
+ if (typeof val === "string") return `"${val.replace(/"/g, '""')}"`;
31
+ return String(val);
32
+ }).join(";")).join("\n");
33
+ return `${header}
34
+ ${body}`;
35
+ }
36
+ function downloadFile(content, filename, mimeType) {
37
+ const blob = new Blob([content], {
38
+ type: mimeType
39
+ });
40
+ const url = URL.createObjectURL(blob);
41
+ const a = document.createElement("a");
42
+ a.href = url;
43
+ a.download = filename;
44
+ a.click();
45
+ URL.revokeObjectURL(url);
46
+ }
47
+ function resolveContent(raw) {
48
+ if (!raw || typeof raw !== "object") return null;
49
+ const obj = raw;
50
+ if (Array.isArray(obj.columns) && Array.isArray(obj.rows)) {
51
+ return obj;
52
+ }
53
+ if (obj.content && typeof obj.content === "object") {
54
+ const inner = obj.content;
55
+ if (Array.isArray(inner.columns) && Array.isArray(inner.rows)) {
56
+ return inner;
57
+ }
58
+ }
59
+ return null;
60
+ }
61
+ function DataPreviewSection(props) {
62
+ const content = createMemo(() => {
63
+ const resolved = resolveContent(props.content);
64
+ if (!resolved) {
65
+ console.warn("[MCP-UI] DataPreviewSection: invalid content — expected { columns: [...], rows: [...] }, got:", props.content);
66
+ }
67
+ return resolved;
68
+ });
69
+ const columns = () => {
70
+ var _a;
71
+ return ((_a = content()) == null ? void 0 : _a.columns) || [];
72
+ };
73
+ const rows = () => {
74
+ var _a;
75
+ return ((_a = content()) == null ? void 0 : _a.rows) || [];
76
+ };
77
+ const pageSize = () => {
78
+ var _a;
79
+ return ((_a = content()) == null ? void 0 : _a.pageSize) || 25;
80
+ };
81
+ const [page, setPage] = createSignal(0);
82
+ const totalRows = () => rows().length;
83
+ const totalPages = () => Math.max(1, Math.ceil(totalRows() / pageSize()));
84
+ const pagedRows = createMemo(() => {
85
+ const start = page() * pageSize();
86
+ return rows().slice(start, start + pageSize());
87
+ });
88
+ const handleExportCSV = () => {
89
+ const c = content();
90
+ if (!c) return;
91
+ const csv = toCSV(c.columns, c.rows);
92
+ downloadFile(csv, "data-export.csv", "text/csv;charset=utf-8");
93
+ };
94
+ const handleExportJSON = () => {
95
+ const json = JSON.stringify(rows(), null, 2);
96
+ downloadFile(json, "data-export.json", "application/json");
97
+ };
98
+ const columnAlign = (col) => {
99
+ if (col.align) return col.align;
100
+ if (col.type === "number") return "right";
101
+ return "left";
102
+ };
103
+ return createComponent(Show, {
104
+ get when() {
105
+ return content();
106
+ },
107
+ get fallback() {
108
+ return getNextElement(_tmpl$);
109
+ },
110
+ children: (c) => (() => {
111
+ var _el$2 = getNextElement(_tmpl$6), _el$3 = _el$2.firstChild, _el$4 = _el$3.firstChild, _el$7 = _el$4.firstChild, [_el$8, _co$] = getNextMarker(_el$7.nextSibling), _el$9 = _el$8.nextSibling, [_el$0, _co$2] = getNextMarker(_el$9.nextSibling), _el$12 = _el$4.nextSibling, [_el$13, _co$3] = getNextMarker(_el$12.nextSibling), _el$14 = _el$3.nextSibling, _el$15 = _el$14.firstChild, _el$16 = _el$15.firstChild, _el$17 = _el$16.firstChild, _el$18 = _el$16.nextSibling, _el$19 = _el$14.nextSibling, _el$20 = _el$19.firstChild, _el$30 = _el$20.nextSibling, [_el$31, _co$6] = getNextMarker(_el$30.nextSibling);
112
+ insert(_el$4, createComponent(Show, {
113
+ get when() {
114
+ return c().source;
115
+ },
116
+ get children() {
117
+ var _el$5 = getNextElement(_tmpl$2);
118
+ insert(_el$5, () => c().source);
119
+ return _el$5;
120
+ }
121
+ }), _el$8, _co$);
122
+ insert(_el$4, createComponent(Show, {
123
+ get when() {
124
+ return c().freshness;
125
+ },
126
+ get children() {
127
+ var _el$6 = getNextElement(_tmpl$3);
128
+ insert(_el$6, () => c().freshness);
129
+ return _el$6;
130
+ }
131
+ }), _el$0, _co$2);
132
+ insert(_el$3, createComponent(Show, {
133
+ get when() {
134
+ return c().exportable !== false;
135
+ },
136
+ get children() {
137
+ var _el$1 = getNextElement(_tmpl$4), _el$10 = _el$1.firstChild, _el$11 = _el$10.nextSibling;
138
+ _el$10.$$click = handleExportCSV;
139
+ _el$11.$$click = handleExportJSON;
140
+ runHydrationEvents();
141
+ return _el$1;
142
+ }
143
+ }), _el$13, _co$3);
144
+ insert(_el$17, createComponent(For, {
145
+ get each() {
146
+ return columns();
147
+ },
148
+ children: (col) => (() => {
149
+ var _el$32 = getNextElement(_tmpl$7);
150
+ insert(_el$32, () => col.label);
151
+ effect((_$p) => setStyleProperty(_el$32, "text-align", columnAlign(col)));
152
+ return _el$32;
153
+ })()
154
+ }));
155
+ insert(_el$18, createComponent(For, {
156
+ get each() {
157
+ return pagedRows();
158
+ },
159
+ children: (row, i) => (() => {
160
+ var _el$33 = getNextElement(_tmpl$8);
161
+ insert(_el$33, createComponent(For, {
162
+ get each() {
163
+ return columns();
164
+ },
165
+ children: (col) => (() => {
166
+ var _el$34 = getNextElement(_tmpl$9);
167
+ insert(_el$34, () => formatCell(row[col.key], col));
168
+ effect((_$p) => setStyleProperty(_el$34, "text-align", columnAlign(col)));
169
+ return _el$34;
170
+ })()
171
+ }));
172
+ effect((_$p) => classList(_el$33, {
173
+ "bg-gray-25 dark:bg-gray-850": i() % 2 === 1
174
+ }, _$p));
175
+ return _el$33;
176
+ })()
177
+ }));
178
+ insert(_el$20, (() => {
179
+ var _c$ = memo(() => !!c().totalRows);
180
+ return () => _c$() ? `${totalRows()} / ${c().totalRows.toLocaleString("fr-FR")} rows` : `${totalRows()} row${totalRows() !== 1 ? "s" : ""}`;
181
+ })());
182
+ insert(_el$19, createComponent(Show, {
183
+ get when() {
184
+ return totalPages() > 1;
185
+ },
186
+ get children() {
187
+ var _el$21 = getNextElement(_tmpl$5), _el$22 = _el$21.firstChild, _el$23 = _el$22.nextSibling, _el$25 = _el$23.firstChild, [_el$26, _co$4] = getNextMarker(_el$25.nextSibling), _el$24 = _el$26.nextSibling, _el$27 = _el$24.nextSibling, [_el$28, _co$5] = getNextMarker(_el$27.nextSibling), _el$29 = _el$23.nextSibling;
188
+ _el$22.$$click = () => setPage((p) => p - 1);
189
+ insert(_el$23, () => page() + 1, _el$26, _co$4);
190
+ insert(_el$23, totalPages, _el$28, _co$5);
191
+ _el$29.$$click = () => setPage((p) => p + 1);
192
+ effect((_p$) => {
193
+ var _v$ = page() === 0, _v$2 = page() >= totalPages() - 1;
194
+ _v$ !== _p$.e && setProperty(_el$22, "disabled", _p$.e = _v$);
195
+ _v$2 !== _p$.t && setProperty(_el$29, "disabled", _p$.t = _v$2);
196
+ return _p$;
197
+ }, {
198
+ e: void 0,
199
+ t: void 0
200
+ });
201
+ runHydrationEvents();
202
+ return _el$21;
203
+ }
204
+ }), _el$31, _co$6);
205
+ return _el$2;
206
+ })()
207
+ });
208
+ }
209
+ delegateEvents(["click"]);
210
+ export {
211
+ DataPreviewSection
212
+ };
213
+ //# sourceMappingURL=DataPreviewSection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DataPreviewSection.js","sources":["../../src/components/DataPreviewSection.tsx"],"sourcesContent":["/**\n * DataPreviewSection — paginated data table with export\n * v4.0.1: Fixed rendering — defensive guards for store proxy content\n *\n * @experimental\n *\n * Features:\n * - Column types (number right-aligned, string left-aligned)\n * - Pagination (configurable page size)\n * - CSV / JSON export buttons\n * - Source attribution + freshness label\n * - Number formatting (FR locale)\n */\n\nimport { createSignal, createMemo, For, Show } from 'solid-js'\nimport type { DataPreviewContent, DataPreviewColumn } from '../types/chat-bus'\n\nexport interface DataPreviewSectionProps {\n content: DataPreviewContent\n}\n\n/** Format a number for display (French locale) */\nfunction formatNumber(value: unknown, format?: string): string {\n if (typeof value !== 'number' || !isFinite(value)) return String(value ?? '')\n if (format === 'percent') return `${(value * 100).toFixed(1)}%`\n if (format === 'currency') return `${value.toLocaleString('fr-FR')} EUR`\n if (Number.isInteger(value)) return value.toLocaleString('fr-FR')\n return value.toLocaleString('fr-FR', { maximumFractionDigits: 2 })\n}\n\n/** Format a cell value based on column type */\nfunction formatCell(value: unknown, col: DataPreviewColumn): string {\n if (value == null) return '\\u2014'\n if (col.type === 'number') return formatNumber(value, col.format)\n if (col.type === 'date' && typeof value === 'string') {\n try {\n return new Date(value).toLocaleDateString('fr-FR')\n } catch {\n return value\n }\n }\n return String(value)\n}\n\n/** Generate CSV from columns + rows */\nfunction toCSV(columns: DataPreviewColumn[], rows: Record<string, unknown>[]): string {\n const header = columns.map(c => `\"${c.label.replace(/\"/g, '\"\"')}\"`).join(';')\n const body = rows.map(row =>\n columns.map(c => {\n const val = row[c.key]\n if (val == null) return ''\n if (typeof val === 'string') return `\"${val.replace(/\"/g, '\"\"')}\"`\n return String(val)\n }).join(';')\n ).join('\\n')\n return `${header}\\n${body}`\n}\n\n/** Trigger browser download */\nfunction downloadFile(content: string, filename: string, mimeType: string) {\n const blob = new Blob([content], { type: mimeType })\n const url = URL.createObjectURL(blob)\n const a = document.createElement('a')\n a.href = url\n a.download = filename\n a.click()\n URL.revokeObjectURL(url)\n}\n\n/**\n * Extract a valid DataPreviewContent from props.content.\n * Handles: direct DataPreviewContent, or wrapped in an extra layer.\n */\nfunction resolveContent(raw: unknown): DataPreviewContent | null {\n if (!raw || typeof raw !== 'object') return null\n const obj = raw as Record<string, unknown>\n\n // Direct shape: { columns: [...], rows: [...] }\n if (Array.isArray(obj.columns) && Array.isArray(obj.rows)) {\n return obj as unknown as DataPreviewContent\n }\n\n // Wrapped shape: { content: { columns: [...], rows: [...] } }\n if (obj.content && typeof obj.content === 'object') {\n const inner = obj.content as Record<string, unknown>\n if (Array.isArray(inner.columns) && Array.isArray(inner.rows)) {\n return inner as unknown as DataPreviewContent\n }\n }\n\n return null\n}\n\nexport function DataPreviewSection(props: DataPreviewSectionProps) {\n const content = createMemo(() => {\n const resolved = resolveContent(props.content)\n if (!resolved) {\n console.warn(\n '[MCP-UI] DataPreviewSection: invalid content — expected { columns: [...], rows: [...] }, got:',\n props.content\n )\n }\n return resolved\n })\n\n const columns = () => content()?.columns || []\n const rows = () => content()?.rows || []\n const pageSize = () => content()?.pageSize || 25\n const [page, setPage] = createSignal(0)\n\n const totalRows = () => rows().length\n const totalPages = () => Math.max(1, Math.ceil(totalRows() / pageSize()))\n\n const pagedRows = createMemo(() => {\n const start = page() * pageSize()\n return rows().slice(start, start + pageSize())\n })\n\n const handleExportCSV = () => {\n const c = content()\n if (!c) return\n const csv = toCSV(c.columns, c.rows)\n downloadFile(csv, 'data-export.csv', 'text/csv;charset=utf-8')\n }\n\n const handleExportJSON = () => {\n const json = JSON.stringify(rows(), null, 2)\n downloadFile(json, 'data-export.json', 'application/json')\n }\n\n const columnAlign = (col: DataPreviewColumn) => {\n if (col.align) return col.align\n if (col.type === 'number') return 'right'\n return 'left'\n }\n\n return (\n <Show when={content()} fallback={\n <div class=\"text-xs text-amber-600 dark:text-amber-400 p-2\">\n [DataPreviewSection] Invalid content format\n </div>\n }>\n {(c) => (\n <div class=\"data-preview-section\">\n {/* Header with source + export */}\n <div class=\"flex items-center justify-between mb-2\">\n <div class=\"flex items-center gap-2 text-xs text-gray-500 dark:text-gray-400\">\n <Show when={c().source}>\n <span class=\"font-medium\">{c().source}</span>\n </Show>\n <Show when={c().freshness}>\n <span class=\"px-1.5 py-0.5 rounded bg-blue-50 dark:bg-blue-900/20 text-blue-700 dark:text-blue-300\">\n {c().freshness}\n </span>\n </Show>\n </div>\n\n <Show when={c().exportable !== false}>\n <div class=\"flex items-center gap-1\">\n <button\n class=\"px-2 py-1 text-xs rounded border border-gray-300 dark:border-gray-600 hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors\"\n onClick={handleExportCSV}\n title=\"Export CSV\"\n >\n CSV\n </button>\n <button\n class=\"px-2 py-1 text-xs rounded border border-gray-300 dark:border-gray-600 hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors\"\n onClick={handleExportJSON}\n title=\"Export JSON\"\n >\n JSON\n </button>\n </div>\n </Show>\n </div>\n\n {/* Table */}\n <div class=\"overflow-x-auto rounded border border-gray-200 dark:border-gray-700\">\n <table class=\"w-full text-sm\">\n <thead>\n <tr class=\"bg-gray-50 dark:bg-gray-800\">\n <For each={columns()}>\n {(col) => (\n <th\n class=\"px-3 py-2 font-medium text-xs text-gray-600 dark:text-gray-400 uppercase tracking-wider border-b border-gray-200 dark:border-gray-700\"\n style={{ \"text-align\": columnAlign(col) }}\n >\n {col.label}\n </th>\n )}\n </For>\n </tr>\n </thead>\n <tbody>\n <For each={pagedRows()}>\n {(row, i) => (\n <tr\n class=\"border-b border-gray-100 dark:border-gray-800 hover:bg-gray-50 dark:hover:bg-gray-800/50 transition-colors\"\n classList={{ 'bg-gray-25 dark:bg-gray-850': i() % 2 === 1 }}\n >\n <For each={columns()}>\n {(col) => (\n <td\n class=\"px-3 py-2 text-gray-800 dark:text-gray-200\"\n style={{ \"text-align\": columnAlign(col) }}\n >\n {formatCell(row[col.key], col)}\n </td>\n )}\n </For>\n </tr>\n )}\n </For>\n </tbody>\n </table>\n </div>\n\n {/* Footer: pagination + row count */}\n <div class=\"flex items-center justify-between mt-2 text-xs text-gray-500 dark:text-gray-400\">\n <span>\n {c().totalRows\n ? `${totalRows()} / ${c().totalRows!.toLocaleString('fr-FR')} rows`\n : `${totalRows()} row${totalRows() !== 1 ? 's' : ''}`}\n </span>\n\n <Show when={totalPages() > 1}>\n <div class=\"flex items-center gap-1\">\n <button\n class=\"px-2 py-1 rounded hover:bg-gray-100 dark:hover:bg-gray-700 disabled:opacity-40 transition-colors\"\n disabled={page() === 0}\n onClick={() => setPage(p => p - 1)}\n >\n &laquo;\n </button>\n <span>{page() + 1} / {totalPages()}</span>\n <button\n class=\"px-2 py-1 rounded hover:bg-gray-100 dark:hover:bg-gray-700 disabled:opacity-40 transition-colors\"\n disabled={page() >= totalPages() - 1}\n onClick={() => setPage(p => p + 1)}\n >\n &raquo;\n </button>\n </div>\n </Show>\n </div>\n </div>\n )}\n </Show>\n )\n}\n"],"names":["formatNumber","value","format","isFinite","String","toFixed","toLocaleString","Number","isInteger","maximumFractionDigits","formatCell","col","type","Date","toLocaleDateString","toCSV","columns","rows","header","map","c","label","replace","join","body","row","val","key","downloadFile","content","filename","mimeType","blob","Blob","url","URL","createObjectURL","a","document","createElement","href","download","click","revokeObjectURL","resolveContent","raw","obj","Array","isArray","inner","DataPreviewSection","props","createMemo","resolved","console","warn","pageSize","page","setPage","createSignal","totalRows","length","totalPages","Math","max","ceil","pagedRows","start","slice","handleExportCSV","csv","handleExportJSON","json","JSON","stringify","columnAlign","align","_$createComponent","Show","when","fallback","_$getNextElement","_tmpl$","children","_el$2","_tmpl$6","_el$3","firstChild","_el$4","_el$7","_el$8","_co$","_$getNextMarker","nextSibling","_el$9","_el$0","_co$2","_el$12","_el$13","_co$3","_el$14","_el$15","_el$16","_el$17","_el$18","_el$19","_el$20","_el$30","_el$31","_co$6","_$insert","source","_el$5","_tmpl$2","freshness","_el$6","_tmpl$3","exportable","_el$1","_tmpl$4","_el$10","_el$11","$$click","_$runHydrationEvents","For","each","_el$32","_tmpl$7","_$effect","_$p","_$setStyleProperty","i","_el$33","_tmpl$8","_el$34","_tmpl$9","_$classList","_c$","_$memo","_el$21","_tmpl$5","_el$22","_el$23","_el$25","_el$26","_co$4","_el$24","_el$27","_el$28","_co$5","_el$29","p","_p$","_v$","_v$2","e","_$setProperty","t","undefined","_$delegateEvents"],"mappings":"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"}
@@ -2,9 +2,97 @@
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const web = require("solid-js/web");
4
4
  const solidJs = require("solid-js");
5
- var _tmpl$ = /* @__PURE__ */ web.template(`<div class="p-4 text-red-500 bg-red-50 dark:bg-red-900/20 text-center">`), _tmpl$2 = /* @__PURE__ */ web.template(`<div class="relative z-0"style=width:100%;z-index:0>`), _tmpl$3 = /* @__PURE__ */ web.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"><!$><!/><!$><!/>`);
5
+ var _tmpl$ = /* @__PURE__ */ web.template(`<div class="p-4 text-red-500 bg-red-50 dark:bg-red-900/20 text-center">`), _tmpl$2 = /* @__PURE__ */ web.template(`<div class="relative z-0"style=width:100%;z-index:0>`), _tmpl$3 = /* @__PURE__ */ web.template(`<div><!$><!/><!$><!/>`);
6
6
  let L = null;
7
7
  let clusterCssLoaded = false;
8
+ function getChoroplethColor(value, scale, fallback) {
9
+ if (value == null || typeof value !== "number" || !isFinite(value)) return fallback;
10
+ if (scale.length === 0) return fallback;
11
+ if (value <= scale[0][0]) return scale[0][1];
12
+ if (value >= scale[scale.length - 1][0]) return scale[scale.length - 1][1];
13
+ for (let i = 1; i < scale.length; i++) {
14
+ if (value <= scale[i][0]) return scale[i][1];
15
+ }
16
+ return scale[scale.length - 1][1];
17
+ }
18
+ function buildStyleFn(style) {
19
+ if (!style) {
20
+ return () => ({
21
+ fillColor: "#3388ff",
22
+ fillOpacity: 0.6,
23
+ color: "#333",
24
+ weight: 1,
25
+ opacity: 1
26
+ });
27
+ }
28
+ return (feature) => {
29
+ let fillColor = style.fillColor || "#3388ff";
30
+ if (style.choroplethField && style.choroplethScale && (feature == null ? void 0 : feature.properties)) {
31
+ const val = feature.properties[style.choroplethField];
32
+ fillColor = getChoroplethColor(val, style.choroplethScale, style.choroplethFallback || "#ccc");
33
+ }
34
+ return {
35
+ fillColor,
36
+ fillOpacity: style.fillOpacity ?? 0.6,
37
+ color: style.strokeColor || "#333",
38
+ weight: style.strokeWeight ?? 1,
39
+ opacity: style.strokeOpacity ?? 1
40
+ };
41
+ };
42
+ }
43
+ function buildPopupContent(feature, popup) {
44
+ if (!popup || !(feature == null ? void 0 : feature.properties)) return null;
45
+ const props = feature.properties;
46
+ if (popup.template) {
47
+ return popup.template.replace(/\{\{(\w+)\}\}/g, (_, key) => {
48
+ const val = props[key];
49
+ return val != null ? String(val) : "";
50
+ });
51
+ }
52
+ const parts = [];
53
+ if (popup.titleField && props[popup.titleField] != null) {
54
+ parts.push(`<strong>${escapeHtml(String(props[popup.titleField]))}</strong>`);
55
+ }
56
+ const fields = popup.fields || Object.keys(props).slice(0, 8);
57
+ for (const key of fields) {
58
+ if (key === popup.titleField) continue;
59
+ const val = props[key];
60
+ if (val == null) continue;
61
+ const formatted = typeof val === "number" ? val.toLocaleString("fr-FR") : String(val);
62
+ parts.push(`<span style="color:#666;font-size:11px">${escapeHtml(key)}</span>: ${escapeHtml(formatted)}`);
63
+ }
64
+ return parts.join("<br/>");
65
+ }
66
+ function escapeHtml(str) {
67
+ return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
68
+ }
69
+ function addGeoJSONLayer(mapInst, leaflet, geojson, style, popup) {
70
+ const styleFn = buildStyleFn(style);
71
+ const layer = leaflet.geoJSON(geojson, {
72
+ style: styleFn,
73
+ pointToLayer: (feature, latlng) => {
74
+ const s = styleFn(feature);
75
+ return leaflet.circleMarker(latlng, {
76
+ radius: 6,
77
+ fillColor: s.fillColor,
78
+ fillOpacity: s.fillOpacity,
79
+ color: s.color,
80
+ weight: s.weight,
81
+ opacity: s.opacity
82
+ });
83
+ },
84
+ onEachFeature: (feature, featureLayer) => {
85
+ const html = buildPopupContent(feature, popup);
86
+ if (html) {
87
+ featureLayer.bindPopup(html, {
88
+ maxWidth: 300
89
+ });
90
+ }
91
+ }
92
+ });
93
+ layer.addTo(mapInst);
94
+ return layer;
95
+ }
8
96
  const MapRenderer = (props) => {
9
97
  let mapContainer;
10
98
  let mapInstance = null;
@@ -15,7 +103,7 @@ const MapRenderer = (props) => {
15
103
  return props.params || ((_a = props.component) == null ? void 0 : _a.params);
16
104
  };
17
105
  solidJs.createEffect(async () => {
18
- var _a, _b, _c;
106
+ var _a, _b, _c, _d, _e;
19
107
  if (web.isServer) return;
20
108
  if (!L) {
21
109
  try {
@@ -58,8 +146,9 @@ const MapRenderer = (props) => {
58
146
  }
59
147
  if (mapInstance && L) {
60
148
  const p = params();
149
+ const allBoundsLayers = [];
61
150
  mapInstance.eachLayer((layer) => {
62
- if (layer instanceof L.Marker || layer._group || layer._featureGroup) {
151
+ if (layer instanceof L.Marker || layer instanceof L.GeoJSON || layer instanceof L.CircleMarker || layer._group || layer._featureGroup) {
63
152
  mapInstance.removeLayer(layer);
64
153
  }
65
154
  });
@@ -83,44 +172,93 @@ const MapRenderer = (props) => {
83
172
  });
84
173
  (_a = p == null ? void 0 : p.markers) == null ? void 0 : _a.forEach((marker) => {
85
174
  const m = L.marker(marker.position);
86
- if (marker.tooltip) {
87
- m.bindTooltip(marker.tooltip);
88
- }
89
- if (marker.popup) {
90
- m.bindPopup(marker.popup);
91
- }
175
+ if (marker.tooltip) m.bindTooltip(marker.tooltip);
176
+ if (marker.popup) m.bindPopup(marker.popup);
92
177
  clusterGroup.addLayer(m);
93
178
  markers.push(m);
94
179
  });
95
180
  mapInstance.addLayer(clusterGroup);
96
- } catch (e) {
97
- console.warn("Failed to load leaflet.markercluster, falling back to regular markers", e);
181
+ } catch {
98
182
  (_b = p == null ? void 0 : p.markers) == null ? void 0 : _b.forEach((marker) => {
99
183
  const m = L.marker(marker.position).addTo(mapInstance);
100
- if (marker.tooltip) {
101
- m.bindTooltip(marker.tooltip);
102
- }
103
- if (marker.popup) {
104
- m.bindPopup(marker.popup);
105
- }
184
+ if (marker.tooltip) m.bindTooltip(marker.tooltip);
185
+ if (marker.popup) m.bindPopup(marker.popup);
106
186
  markers.push(m);
107
187
  });
108
188
  }
109
189
  } else {
110
190
  (_c = p == null ? void 0 : p.markers) == null ? void 0 : _c.forEach((marker) => {
111
191
  const m = L.marker(marker.position).addTo(mapInstance);
112
- if (marker.tooltip) {
113
- m.bindTooltip(marker.tooltip);
114
- }
115
- if (marker.popup) {
116
- m.bindPopup(marker.popup);
117
- }
192
+ if (marker.tooltip) m.bindTooltip(marker.tooltip);
193
+ if (marker.popup) m.bindPopup(marker.popup);
118
194
  markers.push(m);
119
195
  });
120
196
  }
121
- if ((p == null ? void 0 : p.fitBounds) && markers.length > 0) {
122
- const group = L.featureGroup(markers);
123
- mapInstance.fitBounds(group.getBounds().pad(0.1));
197
+ if (markers.length > 0) {
198
+ allBoundsLayers.push(...markers);
199
+ }
200
+ if (p == null ? void 0 : p.geojson) {
201
+ const geoLayer = addGeoJSONLayer(mapInstance, L, p.geojson, p.geojsonStyle, p.popup);
202
+ allBoundsLayers.push(geoLayer);
203
+ }
204
+ if ((p == null ? void 0 : p.layers) && p.layers.length > 0) {
205
+ const overlays = {};
206
+ for (const layerDef of p.layers) {
207
+ const geoLayer = addGeoJSONLayer(mapInstance, L, layerDef.geojson, layerDef.style || (p == null ? void 0 : p.geojsonStyle), layerDef.popup || (p == null ? void 0 : p.popup));
208
+ overlays[layerDef.name] = geoLayer;
209
+ allBoundsLayers.push(geoLayer);
210
+ if (layerDef.visible === false) {
211
+ mapInstance.removeLayer(geoLayer);
212
+ }
213
+ }
214
+ if (Object.keys(overlays).length > 1) {
215
+ L.control.layers(null, overlays, {
216
+ collapsed: true
217
+ }).addTo(mapInstance);
218
+ }
219
+ }
220
+ if (p == null ? void 0 : p.pmtiles) {
221
+ try {
222
+ const protomaps = await Promise.resolve().then(() => require(
223
+ /* @vite-ignore */
224
+ "../node_modules/.pnpm/protomaps-leaflet@4.1.1/node_modules/protomaps-leaflet/dist/esm/index.cjs"
225
+ ));
226
+ const pmConfig = p.pmtiles;
227
+ const paintRules = ((_d = pmConfig.paintRules) == null ? void 0 : _d.map((rule) => ({
228
+ dataLayer: rule.dataLayer,
229
+ symbolizer: new protomaps[rule.symbolizer === "polygon" ? "PolygonSymbolizer" : rule.symbolizer === "line" ? "LineSymbolizer" : "CircleSymbolizer"]({
230
+ fill: rule.color || "#3388ff",
231
+ stroke: rule.color || "#333",
232
+ width: rule.width ?? 1,
233
+ opacity: rule.opacity ?? 0.6
234
+ })
235
+ }))) || [];
236
+ const labelRules = ((_e = pmConfig.labelRules) == null ? void 0 : _e.map((rule) => ({
237
+ dataLayer: rule.dataLayer,
238
+ symbolizer: new protomaps.TextSymbolizer({
239
+ label_props: [rule.textField],
240
+ fontSize: rule.fontSize ?? 12
241
+ })
242
+ }))) || [];
243
+ const pmLayer = protomaps.leafletLayer({
244
+ url: pmConfig.url,
245
+ attribution: pmConfig.attribution,
246
+ paintRules,
247
+ labelRules,
248
+ maxZoom: pmConfig.maxZoom,
249
+ minZoom: pmConfig.minZoom
250
+ });
251
+ pmLayer.addTo(mapInstance);
252
+ } catch (e) {
253
+ console.warn("[MCP-UI] Failed to load protomaps-leaflet for PMTiles:", e);
254
+ }
255
+ }
256
+ if ((p == null ? void 0 : p.fitBounds) && allBoundsLayers.length > 0) {
257
+ const group = L.featureGroup(allBoundsLayers);
258
+ const bounds = group.getBounds();
259
+ if (bounds.isValid()) {
260
+ mapInstance.fitBounds(bounds.pad(0.1));
261
+ }
124
262
  } else if (p == null ? void 0 : p.center) {
125
263
  mapInstance.setView(p.center, p.zoom || mapInstance.getZoom());
126
264
  }
@@ -159,6 +297,10 @@ const MapRenderer = (props) => {
159
297
  return _el$3;
160
298
  }
161
299
  }), _el$7, _co$2);
300
+ web.effect(() => {
301
+ var _a;
302
+ return web.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) || ""}`);
303
+ });
162
304
  return _el$;
163
305
  })();
164
306
  };
@@ -1 +1 @@
1
- {"version":3,"file":"MapRenderer.cjs","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.cjs","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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"}
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * MapRenderer - Interactive map Component
3
- * Sprint 6: Code & Maps
4
- * Sprint Ultimate U.2: Marker Clustering Support
3
+ * Sprint 6: Markers + clustering
4
+ * v3.1.0: GeoJSON, choropleth, popups, multi-layer, PMTiles
5
5
  */
6
6
  import { Component } from 'solid-js';
7
7
  import type { UIComponent, MapComponentParams } from '../types';
@@ -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,CAAA;AAEjF,OAAO,KAAK,EAAE,WAAW,EAAE,kBAAkB,EAAqB,MAAM,UAAU,CAAA;AAOlF,MAAM,WAAW,gBAAgB;IAC7B;;OAEG;IACH,SAAS,CAAC,EAAE,WAAW,CAAA;IAEvB;;OAEG;IACH,MAAM,CAAC,EAAE,kBAAkB,CAAA;CAC9B;AAED,eAAO,MAAM,WAAW,EAAE,SAAS,CAAC,gBAAgB,CA6KnD,CAAA"}
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,CAAA;AAEjF,OAAO,KAAK,EAAE,WAAW,EAAE,kBAAkB,EAAkF,MAAM,UAAU,CAAA;AAO/I,MAAM,WAAW,gBAAgB;IAC7B;;OAEG;IACH,SAAS,CAAC,EAAE,WAAW,CAAA;IAEvB;;OAEG;IACH,MAAM,CAAC,EAAE,kBAAkB,CAAA;CAC9B;AA8ID,eAAO,MAAM,WAAW,EAAE,SAAS,CAAC,gBAAgB,CA0OnD,CAAA"}