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

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.
@@ -2,7 +2,7 @@
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="text-xs text-amber-600 dark:text-amber-400 p-2">[DataPreviewSection] Invalid content format`), _tmpl$2 = /* @__PURE__ */ web.template(`<span class=font-medium>`), _tmpl$3 = /* @__PURE__ */ web.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__ */ web.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__ */ web.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__ */ web.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__ */ web.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__ */ web.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__ */ web.template(`<td class="px-3 py-2 text-gray-800 dark:text-gray-200">`);
5
+ var _tmpl$ = /* @__PURE__ */ web.template(`<div class="text-xs text-amber-600 dark:text-amber-400 p-2">[DataPreviewSection] Invalid content format`), _tmpl$2 = /* @__PURE__ */ web.template(`<span class=font-medium>`), _tmpl$3 = /* @__PURE__ */ web.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__ */ web.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 (sorted)">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 (sorted)">JSON`), _tmpl$5 = /* @__PURE__ */ web.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__ */ web.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__ */ web.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 cursor-pointer select-none hover:bg-gray-100 dark:hover:bg-gray-700/50 transition-colors"><span class="inline-flex items-center gap-1"><!$><!/><span class="text-[10px] leading-none">`), _tmpl$8 = /* @__PURE__ */ web.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__ */ web.template(`<td class="px-3 py-2 text-gray-800 dark:text-gray-200">`);
6
6
  function formatNumber(value, format) {
7
7
  if (typeof value !== "number" || !isFinite(value)) return String(value ?? "");
8
8
  if (format === "percent") return `${(value * 100).toFixed(1)}%`;
@@ -24,6 +24,30 @@ function formatCell(value, col) {
24
24
  }
25
25
  return String(value);
26
26
  }
27
+ function compareValues(a, b, type) {
28
+ if (a == null && b == null) return 0;
29
+ if (a == null) return 1;
30
+ if (b == null) return -1;
31
+ if (type === "number") {
32
+ const na = typeof a === "number" ? a : Number(a);
33
+ const nb = typeof b === "number" ? b : Number(b);
34
+ if (isNaN(na) && isNaN(nb)) return 0;
35
+ if (isNaN(na)) return 1;
36
+ if (isNaN(nb)) return -1;
37
+ return na - nb;
38
+ }
39
+ if (type === "date") {
40
+ const da = new Date(String(a)).getTime();
41
+ const db = new Date(String(b)).getTime();
42
+ if (isNaN(da) && isNaN(db)) return 0;
43
+ if (isNaN(da)) return 1;
44
+ if (isNaN(db)) return -1;
45
+ return da - db;
46
+ }
47
+ return String(a).localeCompare(String(b), "fr", {
48
+ sensitivity: "base"
49
+ });
50
+ }
27
51
  function toCSV(columns, rows) {
28
52
  const header = columns.map((c) => `"${c.label.replace(/"/g, '""')}"`).join(";");
29
53
  const body = rows.map((row) => columns.map((c) => {
@@ -72,7 +96,7 @@ function DataPreviewSection(props) {
72
96
  var _a;
73
97
  return ((_a = content()) == null ? void 0 : _a.columns) || [];
74
98
  };
75
- const rows = () => {
99
+ const rawRows = () => {
76
100
  var _a;
77
101
  return ((_a = content()) == null ? void 0 : _a.rows) || [];
78
102
  };
@@ -81,20 +105,47 @@ function DataPreviewSection(props) {
81
105
  return ((_a = content()) == null ? void 0 : _a.pageSize) || 25;
82
106
  };
83
107
  const [page, setPage] = solidJs.createSignal(0);
84
- const totalRows = () => rows().length;
108
+ const [sortKey, setSortKey] = solidJs.createSignal(null);
109
+ const [sortDir, setSortDir] = solidJs.createSignal(null);
110
+ const handleSort = (key) => {
111
+ if (sortKey() === key) {
112
+ if (sortDir() === "asc") setSortDir("desc");
113
+ else {
114
+ setSortKey(null);
115
+ setSortDir(null);
116
+ }
117
+ } else {
118
+ setSortKey(key);
119
+ setSortDir("asc");
120
+ }
121
+ setPage(0);
122
+ };
123
+ const sortedRows = solidJs.createMemo(() => {
124
+ const r = rawRows();
125
+ const key = sortKey();
126
+ const dir = sortDir();
127
+ if (!key || !dir) return r;
128
+ const col = columns().find((c) => c.key === key);
129
+ const type = col == null ? void 0 : col.type;
130
+ return [...r].sort((a, b) => {
131
+ const cmp = compareValues(a[key], b[key], type);
132
+ return dir === "desc" ? -cmp : cmp;
133
+ });
134
+ });
135
+ const totalRows = () => sortedRows().length;
85
136
  const totalPages = () => Math.max(1, Math.ceil(totalRows() / pageSize()));
86
137
  const pagedRows = solidJs.createMemo(() => {
87
138
  const start = page() * pageSize();
88
- return rows().slice(start, start + pageSize());
139
+ return sortedRows().slice(start, start + pageSize());
89
140
  });
90
141
  const handleExportCSV = () => {
91
142
  const c = content();
92
143
  if (!c) return;
93
- const csv = toCSV(c.columns, c.rows);
144
+ const csv = toCSV(c.columns, sortedRows());
94
145
  downloadFile(csv, "data-export.csv", "text/csv;charset=utf-8");
95
146
  };
96
147
  const handleExportJSON = () => {
97
- const json = JSON.stringify(rows(), null, 2);
148
+ const json = JSON.stringify(sortedRows(), null, 2);
98
149
  downloadFile(json, "data-export.json", "application/json");
99
150
  };
100
151
  const columnAlign = (col) => {
@@ -102,6 +153,10 @@ function DataPreviewSection(props) {
102
153
  if (col.type === "number") return "right";
103
154
  return "left";
104
155
  };
156
+ const sortIndicator = (key) => {
157
+ if (sortKey() !== key) return "↕";
158
+ return sortDir() === "asc" ? "↑" : "↓";
159
+ };
105
160
  return web.createComponent(solidJs.Show, {
106
161
  get when() {
107
162
  return content();
@@ -148,9 +203,25 @@ function DataPreviewSection(props) {
148
203
  return columns();
149
204
  },
150
205
  children: (col) => (() => {
151
- var _el$32 = web.getNextElement(_tmpl$7);
152
- web.insert(_el$32, () => col.label);
153
- web.effect((_$p) => web.setStyleProperty(_el$32, "text-align", columnAlign(col)));
206
+ var _el$32 = web.getNextElement(_tmpl$7), _el$33 = _el$32.firstChild, _el$35 = _el$33.firstChild, [_el$36, _co$7] = web.getNextMarker(_el$35.nextSibling), _el$34 = _el$36.nextSibling;
207
+ _el$32.$$click = () => handleSort(col.key);
208
+ web.insert(_el$33, () => col.label, _el$36, _co$7);
209
+ web.insert(_el$34, () => sortIndicator(col.key));
210
+ web.effect((_p$) => {
211
+ var _v$3 = columnAlign(col), _v$4 = `Sort by ${col.label}`, _v$5 = {
212
+ "opacity-30": sortKey() !== col.key,
213
+ "opacity-100 text-blue-600 dark:text-blue-400": sortKey() === col.key
214
+ };
215
+ _v$3 !== _p$.e && web.setStyleProperty(_el$32, "text-align", _p$.e = _v$3);
216
+ _v$4 !== _p$.t && web.setAttribute(_el$32, "title", _p$.t = _v$4);
217
+ _p$.a = web.classList(_el$34, _v$5, _p$.a);
218
+ return _p$;
219
+ }, {
220
+ e: void 0,
221
+ t: void 0,
222
+ a: void 0
223
+ });
224
+ web.runHydrationEvents();
154
225
  return _el$32;
155
226
  })()
156
227
  }));
@@ -159,22 +230,22 @@ function DataPreviewSection(props) {
159
230
  return pagedRows();
160
231
  },
161
232
  children: (row, i) => (() => {
162
- var _el$33 = web.getNextElement(_tmpl$8);
163
- web.insert(_el$33, web.createComponent(solidJs.For, {
233
+ var _el$37 = web.getNextElement(_tmpl$8);
234
+ web.insert(_el$37, web.createComponent(solidJs.For, {
164
235
  get each() {
165
236
  return columns();
166
237
  },
167
238
  children: (col) => (() => {
168
- var _el$34 = web.getNextElement(_tmpl$9);
169
- web.insert(_el$34, () => formatCell(row[col.key], col));
170
- web.effect((_$p) => web.setStyleProperty(_el$34, "text-align", columnAlign(col)));
171
- return _el$34;
239
+ var _el$38 = web.getNextElement(_tmpl$9);
240
+ web.insert(_el$38, () => formatCell(row[col.key], col));
241
+ web.effect((_$p) => web.setStyleProperty(_el$38, "text-align", columnAlign(col)));
242
+ return _el$38;
172
243
  })()
173
244
  }));
174
- web.effect((_$p) => web.classList(_el$33, {
245
+ web.effect((_$p) => web.classList(_el$37, {
175
246
  "bg-gray-25 dark:bg-gray-850": i() % 2 === 1
176
247
  }, _$p));
177
- return _el$33;
248
+ return _el$37;
178
249
  })()
179
250
  }));
180
251
  web.insert(_el$20, (() => {
@@ -1 +1 @@
1
- {"version":3,"file":"DataPreviewSection.cjs","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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"}
1
+ {"version":3,"file":"DataPreviewSection.cjs","sources":["../../src/components/DataPreviewSection.tsx"],"sourcesContent":["/**\n * DataPreviewSection — paginated, sortable data table with export\n * v4.0.3: Sortable columns (click header: asc → desc → reset)\n *\n * @experimental\n *\n * Features:\n * - Sortable columns (type-aware: number, string, date)\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\ntype SortDir = 'asc' | 'desc' | null\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/** Compare two values for sorting, type-aware */\nfunction compareValues(a: unknown, b: unknown, type?: string): number {\n if (a == null && b == null) return 0\n if (a == null) return 1\n if (b == null) return -1\n\n if (type === 'number') {\n const na = typeof a === 'number' ? a : Number(a)\n const nb = typeof b === 'number' ? b : Number(b)\n if (isNaN(na) && isNaN(nb)) return 0\n if (isNaN(na)) return 1\n if (isNaN(nb)) return -1\n return na - nb\n }\n\n if (type === 'date') {\n const da = new Date(String(a)).getTime()\n const db = new Date(String(b)).getTime()\n if (isNaN(da) && isNaN(db)) return 0\n if (isNaN(da)) return 1\n if (isNaN(db)) return -1\n return da - db\n }\n\n return String(a).localeCompare(String(b), 'fr', { sensitivity: 'base' })\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 if (Array.isArray(obj.columns) && Array.isArray(obj.rows)) {\n return obj as unknown as DataPreviewContent\n }\n\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 rawRows = () => content()?.rows || []\n const pageSize = () => content()?.pageSize || 25\n const [page, setPage] = createSignal(0)\n const [sortKey, setSortKey] = createSignal<string | null>(null)\n const [sortDir, setSortDir] = createSignal<SortDir>(null)\n\n const handleSort = (key: string) => {\n if (sortKey() === key) {\n // Cycle: asc → desc → reset\n if (sortDir() === 'asc') setSortDir('desc')\n else { setSortKey(null); setSortDir(null) }\n } else {\n setSortKey(key)\n setSortDir('asc')\n }\n setPage(0)\n }\n\n const sortedRows = createMemo(() => {\n const r = rawRows()\n const key = sortKey()\n const dir = sortDir()\n if (!key || !dir) return r\n\n const col = columns().find(c => c.key === key)\n const type = col?.type\n\n return [...r].sort((a, b) => {\n const cmp = compareValues(a[key], b[key], type)\n return dir === 'desc' ? -cmp : cmp\n })\n })\n\n const totalRows = () => sortedRows().length\n const totalPages = () => Math.max(1, Math.ceil(totalRows() / pageSize()))\n\n const pagedRows = createMemo(() => {\n const start = page() * pageSize()\n return sortedRows().slice(start, start + pageSize())\n })\n\n const handleExportCSV = () => {\n const c = content()\n if (!c) return\n const csv = toCSV(c.columns, sortedRows())\n downloadFile(csv, 'data-export.csv', 'text/csv;charset=utf-8')\n }\n\n const handleExportJSON = () => {\n const json = JSON.stringify(sortedRows(), 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 const sortIndicator = (key: string) => {\n if (sortKey() !== key) return '\\u2195' // ↕ neutral\n return sortDir() === 'asc' ? '\\u2191' : '\\u2193' // ↑ or ↓\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 (sorted)\"\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 (sorted)\"\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 cursor-pointer select-none hover:bg-gray-100 dark:hover:bg-gray-700/50 transition-colors\"\n style={{ \"text-align\": columnAlign(col) }}\n onClick={() => handleSort(col.key)}\n title={`Sort by ${col.label}`}\n >\n <span class=\"inline-flex items-center gap-1\">\n {col.label}\n <span\n class=\"text-[10px] leading-none\"\n classList={{\n 'opacity-30': sortKey() !== col.key,\n 'opacity-100 text-blue-600 dark:text-blue-400': sortKey() === col.key,\n }}\n >\n {sortIndicator(col.key)}\n </span>\n </span>\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","compareValues","a","b","na","nb","isNaN","da","getTime","db","localeCompare","sensitivity","toCSV","columns","rows","header","map","c","label","replace","join","body","row","val","key","downloadFile","content","filename","mimeType","blob","Blob","url","URL","createObjectURL","document","createElement","href","download","click","revokeObjectURL","resolveContent","raw","obj","Array","isArray","inner","DataPreviewSection","props","createMemo","resolved","console","warn","rawRows","pageSize","page","setPage","createSignal","sortKey","setSortKey","sortDir","setSortDir","handleSort","sortedRows","r","dir","find","sort","cmp","totalRows","length","totalPages","Math","max","ceil","pagedRows","start","slice","handleExportCSV","csv","handleExportJSON","json","JSON","stringify","columnAlign","align","sortIndicator","_$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","_el$33","_el$35","_el$36","_co$7","_el$34","_$effect","_p$","_v$3","_v$4","_v$5","e","_$setStyleProperty","t","_$setAttribute","_$classList","undefined","i","_el$37","_tmpl$8","_el$38","_tmpl$9","_$p","_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","_v$","_v$2","_$setProperty","_$delegateEvents"],"mappings":"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"}
@@ -1,10 +1,11 @@
1
1
  /**
2
- * DataPreviewSection — paginated data table with export
3
- * v4.0.1: Fixed rendering defensive guards for store proxy content
2
+ * DataPreviewSection — paginated, sortable data table with export
3
+ * v4.0.3: Sortable columns (click header: asc desc reset)
4
4
  *
5
5
  * @experimental
6
6
  *
7
7
  * Features:
8
+ * - Sortable columns (type-aware: number, string, date)
8
9
  * - Column types (number right-aligned, string left-aligned)
9
10
  * - Pagination (configurable page size)
10
11
  * - CSV / JSON export buttons
@@ -1 +1 @@
1
- {"version":3,"file":"DataPreviewSection.d.ts","sourceRoot":"","sources":["../../src/components/DataPreviewSection.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,KAAK,EAAE,kBAAkB,EAAqB,MAAM,mBAAmB,CAAA;AAE9E,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,kBAAkB,CAAA;CAC5B;AA0ED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,uBAAuB,kCA6JhE"}
1
+ {"version":3,"file":"DataPreviewSection.d.ts","sourceRoot":"","sources":["../../src/components/DataPreviewSection.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAGH,OAAO,KAAK,EAAE,kBAAkB,EAAqB,MAAM,mBAAmB,CAAA;AAE9E,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,kBAAkB,CAAA;CAC5B;AAqGD,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,uBAAuB,kCA4MhE"}
@@ -1,6 +1,6 @@
1
- import { delegateEvents, createComponent, getNextElement, template, getNextMarker, insert, runHydrationEvents, effect, setStyleProperty, classList, memo, setProperty } from "solid-js/web";
1
+ import { delegateEvents, createComponent, getNextElement, template, getNextMarker, insert, runHydrationEvents, effect, setStyleProperty, setAttribute, classList, memo, setProperty } from "solid-js/web";
2
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">`);
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 (sorted)">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 (sorted)">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 cursor-pointer select-none hover:bg-gray-100 dark:hover:bg-gray-700/50 transition-colors"><span class="inline-flex items-center gap-1"><!$><!/><span class="text-[10px] leading-none">`), _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)}%`;
@@ -22,6 +22,30 @@ function formatCell(value, col) {
22
22
  }
23
23
  return String(value);
24
24
  }
25
+ function compareValues(a, b, type) {
26
+ if (a == null && b == null) return 0;
27
+ if (a == null) return 1;
28
+ if (b == null) return -1;
29
+ if (type === "number") {
30
+ const na = typeof a === "number" ? a : Number(a);
31
+ const nb = typeof b === "number" ? b : Number(b);
32
+ if (isNaN(na) && isNaN(nb)) return 0;
33
+ if (isNaN(na)) return 1;
34
+ if (isNaN(nb)) return -1;
35
+ return na - nb;
36
+ }
37
+ if (type === "date") {
38
+ const da = new Date(String(a)).getTime();
39
+ const db = new Date(String(b)).getTime();
40
+ if (isNaN(da) && isNaN(db)) return 0;
41
+ if (isNaN(da)) return 1;
42
+ if (isNaN(db)) return -1;
43
+ return da - db;
44
+ }
45
+ return String(a).localeCompare(String(b), "fr", {
46
+ sensitivity: "base"
47
+ });
48
+ }
25
49
  function toCSV(columns, rows) {
26
50
  const header = columns.map((c) => `"${c.label.replace(/"/g, '""')}"`).join(";");
27
51
  const body = rows.map((row) => columns.map((c) => {
@@ -70,7 +94,7 @@ function DataPreviewSection(props) {
70
94
  var _a;
71
95
  return ((_a = content()) == null ? void 0 : _a.columns) || [];
72
96
  };
73
- const rows = () => {
97
+ const rawRows = () => {
74
98
  var _a;
75
99
  return ((_a = content()) == null ? void 0 : _a.rows) || [];
76
100
  };
@@ -79,20 +103,47 @@ function DataPreviewSection(props) {
79
103
  return ((_a = content()) == null ? void 0 : _a.pageSize) || 25;
80
104
  };
81
105
  const [page, setPage] = createSignal(0);
82
- const totalRows = () => rows().length;
106
+ const [sortKey, setSortKey] = createSignal(null);
107
+ const [sortDir, setSortDir] = createSignal(null);
108
+ const handleSort = (key) => {
109
+ if (sortKey() === key) {
110
+ if (sortDir() === "asc") setSortDir("desc");
111
+ else {
112
+ setSortKey(null);
113
+ setSortDir(null);
114
+ }
115
+ } else {
116
+ setSortKey(key);
117
+ setSortDir("asc");
118
+ }
119
+ setPage(0);
120
+ };
121
+ const sortedRows = createMemo(() => {
122
+ const r = rawRows();
123
+ const key = sortKey();
124
+ const dir = sortDir();
125
+ if (!key || !dir) return r;
126
+ const col = columns().find((c) => c.key === key);
127
+ const type = col == null ? void 0 : col.type;
128
+ return [...r].sort((a, b) => {
129
+ const cmp = compareValues(a[key], b[key], type);
130
+ return dir === "desc" ? -cmp : cmp;
131
+ });
132
+ });
133
+ const totalRows = () => sortedRows().length;
83
134
  const totalPages = () => Math.max(1, Math.ceil(totalRows() / pageSize()));
84
135
  const pagedRows = createMemo(() => {
85
136
  const start = page() * pageSize();
86
- return rows().slice(start, start + pageSize());
137
+ return sortedRows().slice(start, start + pageSize());
87
138
  });
88
139
  const handleExportCSV = () => {
89
140
  const c = content();
90
141
  if (!c) return;
91
- const csv = toCSV(c.columns, c.rows);
142
+ const csv = toCSV(c.columns, sortedRows());
92
143
  downloadFile(csv, "data-export.csv", "text/csv;charset=utf-8");
93
144
  };
94
145
  const handleExportJSON = () => {
95
- const json = JSON.stringify(rows(), null, 2);
146
+ const json = JSON.stringify(sortedRows(), null, 2);
96
147
  downloadFile(json, "data-export.json", "application/json");
97
148
  };
98
149
  const columnAlign = (col) => {
@@ -100,6 +151,10 @@ function DataPreviewSection(props) {
100
151
  if (col.type === "number") return "right";
101
152
  return "left";
102
153
  };
154
+ const sortIndicator = (key) => {
155
+ if (sortKey() !== key) return "↕";
156
+ return sortDir() === "asc" ? "↑" : "↓";
157
+ };
103
158
  return createComponent(Show, {
104
159
  get when() {
105
160
  return content();
@@ -146,9 +201,25 @@ function DataPreviewSection(props) {
146
201
  return columns();
147
202
  },
148
203
  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)));
204
+ var _el$32 = getNextElement(_tmpl$7), _el$33 = _el$32.firstChild, _el$35 = _el$33.firstChild, [_el$36, _co$7] = getNextMarker(_el$35.nextSibling), _el$34 = _el$36.nextSibling;
205
+ _el$32.$$click = () => handleSort(col.key);
206
+ insert(_el$33, () => col.label, _el$36, _co$7);
207
+ insert(_el$34, () => sortIndicator(col.key));
208
+ effect((_p$) => {
209
+ var _v$3 = columnAlign(col), _v$4 = `Sort by ${col.label}`, _v$5 = {
210
+ "opacity-30": sortKey() !== col.key,
211
+ "opacity-100 text-blue-600 dark:text-blue-400": sortKey() === col.key
212
+ };
213
+ _v$3 !== _p$.e && setStyleProperty(_el$32, "text-align", _p$.e = _v$3);
214
+ _v$4 !== _p$.t && setAttribute(_el$32, "title", _p$.t = _v$4);
215
+ _p$.a = classList(_el$34, _v$5, _p$.a);
216
+ return _p$;
217
+ }, {
218
+ e: void 0,
219
+ t: void 0,
220
+ a: void 0
221
+ });
222
+ runHydrationEvents();
152
223
  return _el$32;
153
224
  })()
154
225
  }));
@@ -157,22 +228,22 @@ function DataPreviewSection(props) {
157
228
  return pagedRows();
158
229
  },
159
230
  children: (row, i) => (() => {
160
- var _el$33 = getNextElement(_tmpl$8);
161
- insert(_el$33, createComponent(For, {
231
+ var _el$37 = getNextElement(_tmpl$8);
232
+ insert(_el$37, createComponent(For, {
162
233
  get each() {
163
234
  return columns();
164
235
  },
165
236
  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;
237
+ var _el$38 = getNextElement(_tmpl$9);
238
+ insert(_el$38, () => formatCell(row[col.key], col));
239
+ effect((_$p) => setStyleProperty(_el$38, "text-align", columnAlign(col)));
240
+ return _el$38;
170
241
  })()
171
242
  }));
172
- effect((_$p) => classList(_el$33, {
243
+ effect((_$p) => classList(_el$37, {
173
244
  "bg-gray-25 dark:bg-gray-850": i() % 2 === 1
174
245
  }, _$p));
175
- return _el$33;
246
+ return _el$37;
176
247
  })()
177
248
  }));
178
249
  insert(_el$20, (() => {
@@ -1 +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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"}
1
+ {"version":3,"file":"DataPreviewSection.js","sources":["../../src/components/DataPreviewSection.tsx"],"sourcesContent":["/**\n * DataPreviewSection — paginated, sortable data table with export\n * v4.0.3: Sortable columns (click header: asc → desc → reset)\n *\n * @experimental\n *\n * Features:\n * - Sortable columns (type-aware: number, string, date)\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\ntype SortDir = 'asc' | 'desc' | null\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/** Compare two values for sorting, type-aware */\nfunction compareValues(a: unknown, b: unknown, type?: string): number {\n if (a == null && b == null) return 0\n if (a == null) return 1\n if (b == null) return -1\n\n if (type === 'number') {\n const na = typeof a === 'number' ? a : Number(a)\n const nb = typeof b === 'number' ? b : Number(b)\n if (isNaN(na) && isNaN(nb)) return 0\n if (isNaN(na)) return 1\n if (isNaN(nb)) return -1\n return na - nb\n }\n\n if (type === 'date') {\n const da = new Date(String(a)).getTime()\n const db = new Date(String(b)).getTime()\n if (isNaN(da) && isNaN(db)) return 0\n if (isNaN(da)) return 1\n if (isNaN(db)) return -1\n return da - db\n }\n\n return String(a).localeCompare(String(b), 'fr', { sensitivity: 'base' })\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 if (Array.isArray(obj.columns) && Array.isArray(obj.rows)) {\n return obj as unknown as DataPreviewContent\n }\n\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 rawRows = () => content()?.rows || []\n const pageSize = () => content()?.pageSize || 25\n const [page, setPage] = createSignal(0)\n const [sortKey, setSortKey] = createSignal<string | null>(null)\n const [sortDir, setSortDir] = createSignal<SortDir>(null)\n\n const handleSort = (key: string) => {\n if (sortKey() === key) {\n // Cycle: asc → desc → reset\n if (sortDir() === 'asc') setSortDir('desc')\n else { setSortKey(null); setSortDir(null) }\n } else {\n setSortKey(key)\n setSortDir('asc')\n }\n setPage(0)\n }\n\n const sortedRows = createMemo(() => {\n const r = rawRows()\n const key = sortKey()\n const dir = sortDir()\n if (!key || !dir) return r\n\n const col = columns().find(c => c.key === key)\n const type = col?.type\n\n return [...r].sort((a, b) => {\n const cmp = compareValues(a[key], b[key], type)\n return dir === 'desc' ? -cmp : cmp\n })\n })\n\n const totalRows = () => sortedRows().length\n const totalPages = () => Math.max(1, Math.ceil(totalRows() / pageSize()))\n\n const pagedRows = createMemo(() => {\n const start = page() * pageSize()\n return sortedRows().slice(start, start + pageSize())\n })\n\n const handleExportCSV = () => {\n const c = content()\n if (!c) return\n const csv = toCSV(c.columns, sortedRows())\n downloadFile(csv, 'data-export.csv', 'text/csv;charset=utf-8')\n }\n\n const handleExportJSON = () => {\n const json = JSON.stringify(sortedRows(), 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 const sortIndicator = (key: string) => {\n if (sortKey() !== key) return '\\u2195' // ↕ neutral\n return sortDir() === 'asc' ? '\\u2191' : '\\u2193' // ↑ or ↓\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 (sorted)\"\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 (sorted)\"\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 cursor-pointer select-none hover:bg-gray-100 dark:hover:bg-gray-700/50 transition-colors\"\n style={{ \"text-align\": columnAlign(col) }}\n onClick={() => handleSort(col.key)}\n title={`Sort by ${col.label}`}\n >\n <span class=\"inline-flex items-center gap-1\">\n {col.label}\n <span\n class=\"text-[10px] leading-none\"\n classList={{\n 'opacity-30': sortKey() !== col.key,\n 'opacity-100 text-blue-600 dark:text-blue-400': sortKey() === col.key,\n }}\n >\n {sortIndicator(col.key)}\n </span>\n </span>\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","compareValues","a","b","na","nb","isNaN","da","getTime","db","localeCompare","sensitivity","toCSV","columns","rows","header","map","c","label","replace","join","body","row","val","key","downloadFile","content","filename","mimeType","blob","Blob","url","URL","createObjectURL","document","createElement","href","download","click","revokeObjectURL","resolveContent","raw","obj","Array","isArray","inner","DataPreviewSection","props","createMemo","resolved","console","warn","rawRows","pageSize","page","setPage","createSignal","sortKey","setSortKey","sortDir","setSortDir","handleSort","sortedRows","r","dir","find","sort","cmp","totalRows","length","totalPages","Math","max","ceil","pagedRows","start","slice","handleExportCSV","csv","handleExportJSON","json","JSON","stringify","columnAlign","align","sortIndicator","_$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","_el$33","_el$35","_el$36","_co$7","_el$34","_$effect","_p$","_v$3","_v$4","_v$5","e","_$setStyleProperty","t","_$setAttribute","_$classList","undefined","i","_el$37","_tmpl$8","_el$38","_tmpl$9","_$p","_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","_v$","_v$2","_$setProperty","_$delegateEvents"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seed-ship/mcp-ui-solid",
3
- "version": "4.0.2",
3
+ "version": "4.0.3",
4
4
  "description": "SolidJS components for rendering MCP-generated UI resources",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",