@seed-ship/mcp-ui-solid 4.0.0 → 4.0.2

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.
@@ -1,6 +1,6 @@
1
- import { delegateEvents, getNextElement, template, getNextMarker, insert, createComponent, runHydrationEvents, effect, setStyleProperty, classList, memo, setProperty } from "solid-js/web";
2
- import { createSignal, createMemo, Show, For } from "solid-js";
3
- var _tmpl$ = /* @__PURE__ */ template(`<span class=font-medium>`), _tmpl$2 = /* @__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$3 = /* @__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$4 = /* @__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$5 = /* @__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$6 = /* @__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$7 = /* @__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$8 = /* @__PURE__ */ template(`<td class="px-3 py-2 text-gray-800 dark:text-gray-200">`);
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
4
  function formatNumber(value, format) {
5
5
  if (typeof value !== "number" || !isFinite(value)) return String(value ?? "");
6
6
  if (format === "percent") return `${(value * 100).toFixed(1)}%`;
@@ -44,22 +44,55 @@ function downloadFile(content, filename, mimeType) {
44
44
  a.click();
45
45
  URL.revokeObjectURL(url);
46
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
+ }
47
61
  function DataPreviewSection(props) {
48
- const content = () => props.content;
49
- const pageSize = () => content().pageSize || 25;
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
+ };
50
81
  const [page, setPage] = createSignal(0);
51
- const totalRows = () => content().rows.length;
82
+ const totalRows = () => rows().length;
52
83
  const totalPages = () => Math.max(1, Math.ceil(totalRows() / pageSize()));
53
84
  const pagedRows = createMemo(() => {
54
85
  const start = page() * pageSize();
55
- return content().rows.slice(start, start + pageSize());
86
+ return rows().slice(start, start + pageSize());
56
87
  });
57
88
  const handleExportCSV = () => {
58
- const csv = toCSV(content().columns, content().rows);
89
+ const c = content();
90
+ if (!c) return;
91
+ const csv = toCSV(c.columns, c.rows);
59
92
  downloadFile(csv, "data-export.csv", "text/csv;charset=utf-8");
60
93
  };
61
94
  const handleExportJSON = () => {
62
- const json = JSON.stringify(content().rows, null, 2);
95
+ const json = JSON.stringify(rows(), null, 2);
63
96
  downloadFile(json, "data-export.json", "application/json");
64
97
  };
65
98
  const columnAlign = (col) => {
@@ -67,103 +100,111 @@ function DataPreviewSection(props) {
67
100
  if (col.type === "number") return "right";
68
101
  return "left";
69
102
  };
70
- return (() => {
71
- var _el$ = getNextElement(_tmpl$5), _el$2 = _el$.firstChild, _el$3 = _el$2.firstChild, _el$6 = _el$3.firstChild, [_el$7, _co$] = getNextMarker(_el$6.nextSibling), _el$8 = _el$7.nextSibling, [_el$9, _co$2] = getNextMarker(_el$8.nextSibling), _el$11 = _el$3.nextSibling, [_el$12, _co$3] = getNextMarker(_el$11.nextSibling), _el$13 = _el$2.nextSibling, _el$14 = _el$13.firstChild, _el$15 = _el$14.firstChild, _el$16 = _el$15.firstChild, _el$17 = _el$15.nextSibling, _el$18 = _el$13.nextSibling, _el$19 = _el$18.firstChild, _el$29 = _el$19.nextSibling, [_el$30, _co$6] = getNextMarker(_el$29.nextSibling);
72
- insert(_el$3, createComponent(Show, {
73
- get when() {
74
- return content().source;
75
- },
76
- get children() {
77
- var _el$4 = getNextElement(_tmpl$);
78
- insert(_el$4, () => content().source);
79
- return _el$4;
80
- }
81
- }), _el$7, _co$);
82
- insert(_el$3, createComponent(Show, {
83
- get when() {
84
- return content().freshness;
85
- },
86
- get children() {
87
- var _el$5 = getNextElement(_tmpl$2);
88
- insert(_el$5, () => content().freshness);
89
- return _el$5;
90
- }
91
- }), _el$9, _co$2);
92
- insert(_el$2, createComponent(Show, {
93
- get when() {
94
- return content().exportable !== false;
95
- },
96
- get children() {
97
- var _el$0 = getNextElement(_tmpl$3), _el$1 = _el$0.firstChild, _el$10 = _el$1.nextSibling;
98
- _el$1.$$click = handleExportCSV;
99
- _el$10.$$click = handleExportJSON;
100
- runHydrationEvents();
101
- return _el$0;
102
- }
103
- }), _el$12, _co$3);
104
- insert(_el$16, createComponent(For, {
105
- get each() {
106
- return content().columns;
107
- },
108
- children: (col) => (() => {
109
- var _el$31 = getNextElement(_tmpl$6);
110
- insert(_el$31, () => col.label);
111
- effect((_$p) => setStyleProperty(_el$31, "text-align", columnAlign(col)));
112
- return _el$31;
113
- })()
114
- }));
115
- insert(_el$17, createComponent(For, {
116
- get each() {
117
- return pagedRows();
118
- },
119
- children: (row, i) => (() => {
120
- var _el$32 = getNextElement(_tmpl$7);
121
- insert(_el$32, createComponent(For, {
122
- get each() {
123
- return content().columns;
124
- },
125
- children: (col) => (() => {
126
- var _el$33 = getNextElement(_tmpl$8);
127
- insert(_el$33, () => formatCell(row[col.key], col));
128
- effect((_$p) => setStyleProperty(_el$33, "text-align", columnAlign(col)));
129
- return _el$33;
130
- })()
131
- }));
132
- effect((_$p) => classList(_el$32, {
133
- "bg-gray-25 dark:bg-gray-850": i() % 2 === 1
134
- }, _$p));
135
- return _el$32;
136
- })()
137
- }));
138
- insert(_el$19, (() => {
139
- var _c$ = memo(() => !!content().totalRows);
140
- return () => _c$() ? `${totalRows()} / ${content().totalRows.toLocaleString("fr-FR")} rows` : `${totalRows()} row${totalRows() !== 1 ? "s" : ""}`;
141
- })());
142
- insert(_el$18, createComponent(Show, {
143
- get when() {
144
- return totalPages() > 1;
145
- },
146
- get children() {
147
- var _el$20 = getNextElement(_tmpl$4), _el$21 = _el$20.firstChild, _el$22 = _el$21.nextSibling, _el$24 = _el$22.firstChild, [_el$25, _co$4] = getNextMarker(_el$24.nextSibling), _el$23 = _el$25.nextSibling, _el$26 = _el$23.nextSibling, [_el$27, _co$5] = getNextMarker(_el$26.nextSibling), _el$28 = _el$22.nextSibling;
148
- _el$21.$$click = () => setPage((p) => p - 1);
149
- insert(_el$22, () => page() + 1, _el$25, _co$4);
150
- insert(_el$22, totalPages, _el$27, _co$5);
151
- _el$28.$$click = () => setPage((p) => p + 1);
152
- effect((_p$) => {
153
- var _v$ = page() === 0, _v$2 = page() >= totalPages() - 1;
154
- _v$ !== _p$.e && setProperty(_el$21, "disabled", _p$.e = _v$);
155
- _v$2 !== _p$.t && setProperty(_el$28, "disabled", _p$.t = _v$2);
156
- return _p$;
157
- }, {
158
- e: void 0,
159
- t: void 0
160
- });
161
- runHydrationEvents();
162
- return _el$20;
163
- }
164
- }), _el$30, _co$6);
165
- return _el$;
166
- })();
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
+ });
167
208
  }
168
209
  delegateEvents(["click"]);
169
210
  export {
@@ -1 +1 @@
1
- {"version":3,"file":"DataPreviewSection.js","sources":["../../src/components/DataPreviewSection.tsx"],"sourcesContent":["/**\n * DataPreviewSection — paginated data table with export\n * v3.1.0: Replaces LLM-generated markdown tables with exact source data\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 // Simple formatting: use locale\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 '—'\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\nexport function DataPreviewSection(props: DataPreviewSectionProps) {\n const content = () => props.content\n const pageSize = () => content().pageSize || 25\n const [page, setPage] = createSignal(0)\n\n const totalRows = () => content().rows.length\n const totalPages = () => Math.max(1, Math.ceil(totalRows() / pageSize()))\n\n const pagedRows = createMemo(() => {\n const start = page() * pageSize()\n return content().rows.slice(start, start + pageSize())\n })\n\n const handleExportCSV = () => {\n const csv = toCSV(content().columns, content().rows)\n downloadFile(csv, 'data-export.csv', 'text/csv;charset=utf-8')\n }\n\n const handleExportJSON = () => {\n const json = JSON.stringify(content().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 <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={content().source}>\n <span class=\"font-medium\">{content().source}</span>\n </Show>\n <Show when={content().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 {content().freshness}\n </span>\n </Show>\n </div>\n\n <Show when={content().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={content().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={content().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 {content().totalRows\n ? `${totalRows()} / ${content().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}\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","DataPreviewSection","props","pageSize","page","setPage","createSignal","totalRows","length","totalPages","Math","max","ceil","pagedRows","createMemo","start","slice","handleExportCSV","csv","handleExportJSON","json","JSON","stringify","columnAlign","align","_el$","_$getNextElement","_tmpl$5","_el$2","firstChild","_el$3","_el$6","_el$7","_co$","_$getNextMarker","nextSibling","_el$8","_el$9","_co$2","_el$11","_el$12","_co$3","_el$13","_el$14","_el$15","_el$16","_el$17","_el$18","_el$19","_el$29","_el$30","_co$6","_$insert","_$createComponent","Show","when","source","children","_el$4","_tmpl$","freshness","_el$5","_tmpl$2","exportable","_el$0","_tmpl$3","_el$1","_el$10","$$click","_$runHydrationEvents","For","each","_el$31","_tmpl$6","_$effect","_$p","_$setStyleProperty","i","_el$32","_tmpl$7","_el$33","_tmpl$8","_$classList","_c$","_$memo","_el$20","_tmpl$4","_el$21","_el$22","_el$24","_el$25","_co$4","_el$23","_el$26","_el$27","_co$5","_el$28","p","_p$","_v$","_v$2","e","_$setProperty","t","undefined","_$delegateEvents"],"mappings":"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"}
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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"}
@@ -437,6 +437,16 @@ const ScratchpadPanel = (props) => {
437
437
  return _el$;
438
438
  })();
439
439
  };
440
+ function parseContent(content) {
441
+ if (typeof content === "string") {
442
+ try {
443
+ return JSON.parse(content);
444
+ } catch {
445
+ return content;
446
+ }
447
+ }
448
+ return content;
449
+ }
440
450
  const SectionRenderer = (props) => {
441
451
  return (() => {
442
452
  var _el$99 = web.getNextElement(_tmpl$24), _el$100 = _el$99.firstChild, _el$103 = _el$100.nextSibling, [_el$104, _co$21] = web.getNextMarker(_el$103.nextSibling);
@@ -450,7 +460,7 @@ const SectionRenderer = (props) => {
450
460
  get children() {
451
461
  return web.createComponent(DataSection, {
452
462
  get content() {
453
- return props.section.content;
463
+ return parseContent(props.section.content);
454
464
  }
455
465
  });
456
466
  }
@@ -461,7 +471,7 @@ const SectionRenderer = (props) => {
461
471
  get children() {
462
472
  return web.createComponent(InteractiveFilterSection, {
463
473
  get content() {
464
- return props.section.content;
474
+ return parseContent(props.section.content);
465
475
  },
466
476
  get filters() {
467
477
  return props.filters;
@@ -487,7 +497,7 @@ const SectionRenderer = (props) => {
487
497
  get children() {
488
498
  return web.createComponent(ActionSection, {
489
499
  get content() {
490
- return props.section.content;
500
+ return parseContent(props.section.content);
491
501
  },
492
502
  get onAction() {
493
503
  return props.onAction;
@@ -501,7 +511,7 @@ const SectionRenderer = (props) => {
501
511
  get children() {
502
512
  return web.createComponent(EnrichedStepsSection, {
503
513
  get content() {
504
- return props.section.content;
514
+ return parseContent(props.section.content);
505
515
  },
506
516
  get onAction() {
507
517
  return props.onAction;
@@ -518,7 +528,7 @@ const SectionRenderer = (props) => {
518
528
  get children() {
519
529
  return web.createComponent(EmbeddedFormSection, {
520
530
  get content() {
521
- return props.section.content;
531
+ return parseContent(props.section.content);
522
532
  },
523
533
  get sectionId() {
524
534
  return props.section.id;
@@ -538,7 +548,7 @@ const SectionRenderer = (props) => {
538
548
  get children() {
539
549
  return web.createComponent(UnderstandingSection, {
540
550
  get content() {
541
- return props.section.content;
551
+ return parseContent(props.section.content);
542
552
  }
543
553
  });
544
554
  }
@@ -549,7 +559,7 @@ const SectionRenderer = (props) => {
549
559
  get children() {
550
560
  return web.createComponent(FeedbackSection, {
551
561
  get content() {
552
- return props.section.content;
562
+ return parseContent(props.section.content);
553
563
  },
554
564
  get onAction() {
555
565
  return props.onAction;
@@ -563,7 +573,7 @@ const SectionRenderer = (props) => {
563
573
  get children() {
564
574
  return web.createComponent(PromptSection, {
565
575
  get content() {
566
- return props.section.content;
576
+ return parseContent(props.section.content);
567
577
  },
568
578
  get onAction() {
569
579
  return props.onAction;
@@ -577,7 +587,7 @@ const SectionRenderer = (props) => {
577
587
  get children() {
578
588
  return web.createComponent(StepperProgressSection, {
579
589
  get content() {
580
- return props.section.content;
590
+ return parseContent(props.section.content);
581
591
  }
582
592
  });
583
593
  }
@@ -588,7 +598,7 @@ const SectionRenderer = (props) => {
588
598
  get children() {
589
599
  return web.createComponent(ErrorSectionRenderer, {
590
600
  get content() {
591
- return props.section.content;
601
+ return parseContent(props.section.content);
592
602
  },
593
603
  get onAction() {
594
604
  return props.onAction;
@@ -602,7 +612,7 @@ const SectionRenderer = (props) => {
602
612
  get children() {
603
613
  return web.createComponent(SourceCardSection, {
604
614
  get content() {
605
- return props.section.content;
615
+ return parseContent(props.section.content);
606
616
  }
607
617
  });
608
618
  }
@@ -613,7 +623,7 @@ const SectionRenderer = (props) => {
613
623
  get children() {
614
624
  return web.createComponent(DiffSection, {
615
625
  get content() {
616
- return props.section.content;
626
+ return parseContent(props.section.content);
617
627
  }
618
628
  });
619
629
  }
@@ -622,7 +632,7 @@ const SectionRenderer = (props) => {
622
632
  return props.section.type === "verified_text";
623
633
  },
624
634
  get children() {
625
- return web.createComponent(VerifiedText.VerifiedText, web.mergeProps(() => props.section.content, {
635
+ return web.createComponent(VerifiedText.VerifiedText, web.mergeProps(() => parseContent(props.section.content), {
626
636
  onHallucinationClick: (h) => {
627
637
  var _a;
628
638
  return (_a = props.onAction) == null ? void 0 : _a.call(props, "hallucination_click", h);
@@ -636,7 +646,7 @@ const SectionRenderer = (props) => {
636
646
  get children() {
637
647
  return web.createComponent(DataPreviewSection.DataPreviewSection, {
638
648
  get content() {
639
- return props.section.content;
649
+ return parseContent(props.section.content);
640
650
  }
641
651
  });
642
652
  }
@@ -646,7 +656,7 @@ const SectionRenderer = (props) => {
646
656
  },
647
657
  get children() {
648
658
  return (() => {
649
- const c = props.section.content;
659
+ const c = parseContent(props.section.content);
650
660
  return web.createComponent(MapRenderer.MapRenderer, {
651
661
  get params() {
652
662
  return {
@@ -668,24 +678,26 @@ const SectionRenderer = (props) => {
668
678
  return props.section.type === "chart";
669
679
  },
670
680
  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
- });
681
+ return (() => {
682
+ const c = parseContent(props.section.content);
683
+ return web.createComponent(ChartJSRenderer.ChartJSRenderer, {
684
+ get component() {
685
+ return {
686
+ id: props.section.id,
687
+ type: "chart",
688
+ position: {
689
+ colStart: 1,
690
+ colSpan: 12
691
+ },
692
+ params: {
693
+ ...c,
694
+ renderer: "native",
695
+ height: (c == null ? void 0 : c.height) || "250px"
696
+ }
697
+ };
698
+ }
699
+ });
700
+ })();
689
701
  }
690
702
  }), web.createComponent(solidJs.Match, {
691
703
  when: true,
@@ -1065,7 +1077,15 @@ const EnrichedStepsSection = (props) => {
1065
1077
  })();
1066
1078
  };
1067
1079
  const ActionSection = (props) => {
1068
- const actions = () => Array.isArray(props.content) ? props.content : [];
1080
+ const actions = () => {
1081
+ if (Array.isArray(props.content)) return props.content;
1082
+ const obj = props.content;
1083
+ if (obj && Array.isArray(obj.actions)) {
1084
+ console.warn("[MCP-UI] ActionSection: content should be an array, got { actions: [...] }. Unwrapping automatically.");
1085
+ return obj.actions;
1086
+ }
1087
+ return [];
1088
+ };
1069
1089
  return (() => {
1070
1090
  var _el$178 = web.getNextElement(_tmpl$45);
1071
1091
  web.insert(_el$178, web.createComponent(solidJs.For, {