@orchestra-mcp/widgets 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs ADDED
@@ -0,0 +1,816 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __esm = (fn, res) => function __init() {
9
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
10
+ };
11
+ var __export = (target, all) => {
12
+ for (var name in all)
13
+ __defProp(target, name, { get: all[name], enumerable: true });
14
+ };
15
+ var __copyProps = (to, from, except, desc) => {
16
+ if (from && typeof from === "object" || typeof from === "function") {
17
+ for (let key of __getOwnPropNames(from))
18
+ if (!__hasOwnProp.call(to, key) && key !== except)
19
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
20
+ }
21
+ return to;
22
+ };
23
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
24
+ // If the importer is in node compatibility mode or this is not an ESM
25
+ // file that has been converted to a CommonJS file using a Babel-
26
+ // compatible transform (i.e. "__esModule" has not been set), then set
27
+ // "default" to the CommonJS "module.exports" for node compatibility.
28
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
29
+ mod
30
+ ));
31
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
32
+
33
+ // src/utils/saveFile.ts
34
+ function uuid() {
35
+ return crypto.randomUUID?.() ?? `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
36
+ }
37
+ function uuidFilename(prefix, ext) {
38
+ return `${prefix}-${uuid()}.${ext}`;
39
+ }
40
+ async function saveViaDesktopAPI(content, filename, encoding = "text") {
41
+ try {
42
+ const res = await fetch(`${API}/api/download`, {
43
+ method: "POST",
44
+ headers: { "Content-Type": "application/json" },
45
+ body: JSON.stringify({ filename, content, encoding })
46
+ });
47
+ if (res.ok) {
48
+ const data = await res.json();
49
+ return data.ok !== false;
50
+ }
51
+ } catch {
52
+ }
53
+ return false;
54
+ }
55
+ function getExtension(filename) {
56
+ const dot = filename.lastIndexOf(".");
57
+ return dot >= 0 ? filename.slice(dot) : "";
58
+ }
59
+ async function saveWithPicker(blob, suggestedName, mimeType) {
60
+ if (typeof window === "undefined" || !("showSaveFilePicker" in window)) return false;
61
+ try {
62
+ const ext = getExtension(suggestedName);
63
+ const accept = {};
64
+ if (MIME_EXTENSIONS[mimeType]) {
65
+ accept[mimeType] = MIME_EXTENSIONS[mimeType];
66
+ } else if (ext) {
67
+ accept[mimeType || "application/octet-stream"] = [ext];
68
+ }
69
+ const handle = await window.showSaveFilePicker({
70
+ suggestedName,
71
+ types: Object.keys(accept).length ? [{ description: suggestedName, accept }] : void 0
72
+ });
73
+ const writable = await handle.createWritable();
74
+ await writable.write(blob);
75
+ await writable.close();
76
+ return true;
77
+ } catch (err) {
78
+ if (err?.name === "AbortError") return true;
79
+ return false;
80
+ }
81
+ }
82
+ function saveViaBrowser(blob, filename) {
83
+ const url = URL.createObjectURL(blob);
84
+ const link = document.createElement("a");
85
+ link.href = url;
86
+ link.download = filename;
87
+ link.style.display = "none";
88
+ document.body.appendChild(link);
89
+ link.click();
90
+ setTimeout(() => {
91
+ document.body.removeChild(link);
92
+ URL.revokeObjectURL(url);
93
+ }, 100);
94
+ }
95
+ async function saveFile(content, filename, mimeType) {
96
+ const apiOk = await saveViaDesktopAPI(content, filename);
97
+ if (apiOk) return;
98
+ const blob = new Blob([content], { type: mimeType });
99
+ const pickerOk = await saveWithPicker(blob, filename, mimeType);
100
+ if (pickerOk) return;
101
+ saveViaBrowser(blob, filename);
102
+ }
103
+ async function saveBlob(blob, filename, mimeType) {
104
+ try {
105
+ const buffer = await blob.arrayBuffer();
106
+ const bytes = new Uint8Array(buffer);
107
+ let binary = "";
108
+ for (let i = 0; i < bytes.length; i++) binary += String.fromCharCode(bytes[i]);
109
+ const base64 = btoa(binary);
110
+ const apiOk = await saveViaDesktopAPI(base64, filename, "base64");
111
+ if (apiOk) return;
112
+ } catch {
113
+ }
114
+ const pickerOk = await saveWithPicker(blob, filename, mimeType);
115
+ if (pickerOk) return;
116
+ saveViaBrowser(blob, filename);
117
+ }
118
+ var API, MIME_EXTENSIONS;
119
+ var init_saveFile = __esm({
120
+ "src/utils/saveFile.ts"() {
121
+ "use strict";
122
+ API = "http://127.0.0.1:19191";
123
+ MIME_EXTENSIONS = {
124
+ "text/plain": [".txt"],
125
+ "text/csv": [".csv"],
126
+ "text/html": [".html"],
127
+ "text/css": [".css"],
128
+ "text/javascript": [".js"],
129
+ "text/markdown": [".md"],
130
+ "application/json": [".json"],
131
+ "image/png": [".png"],
132
+ "image/jpeg": [".jpg", ".jpeg"],
133
+ "image/svg+xml": [".svg"]
134
+ };
135
+ }
136
+ });
137
+
138
+ // src/utils/exportToImage.ts
139
+ var exportToImage_exports = {};
140
+ __export(exportToImage_exports, {
141
+ exportToImage: () => exportToImage
142
+ });
143
+ async function exportToImage(element, filename = "export", padding = 40) {
144
+ const { toPng } = await import("html-to-image");
145
+ const computed = getComputedStyle(element);
146
+ const accent = computed.getPropertyValue("--color-accent").trim() || "#7c3aed";
147
+ const bgColor = computed.getPropertyValue("--color-bg").trim() || "#1e1e2e";
148
+ const bgContrast = computed.getPropertyValue("--color-bg-contrast").trim() || bgColor;
149
+ const fgColor = computed.getPropertyValue("--color-fg").trim() || "#e4e4e7";
150
+ const fgMuted = computed.getPropertyValue("--color-fg-muted").trim() || "#a1a1aa";
151
+ const borderColor = computed.getPropertyValue("--color-border").trim() || "#27272a";
152
+ const gradient = `linear-gradient(135deg, ${accent}22 0%, ${bgContrast} 40%, ${bgColor} 100%)`;
153
+ const wrapper = document.createElement("div");
154
+ wrapper.style.display = "inline-block";
155
+ wrapper.style.padding = `${padding}px`;
156
+ wrapper.style.borderRadius = "16px";
157
+ wrapper.style.background = gradient;
158
+ wrapper.style.setProperty("--color-accent", accent);
159
+ wrapper.style.setProperty("--color-bg", bgColor);
160
+ wrapper.style.setProperty("--color-bg-contrast", bgContrast);
161
+ wrapper.style.setProperty("--color-fg", fgColor);
162
+ wrapper.style.setProperty("--color-fg-muted", fgMuted);
163
+ wrapper.style.setProperty("--color-border", borderColor);
164
+ const parent = element.parentNode;
165
+ const nextSibling = element.nextSibling;
166
+ wrapper.appendChild(element);
167
+ document.body.appendChild(wrapper);
168
+ element.classList.add("code-block--exporting");
169
+ element.classList.add("data-table--exporting");
170
+ const origBorderRadius = element.style.borderRadius;
171
+ element.style.borderRadius = "12px";
172
+ try {
173
+ const dataUrl = await toPng(wrapper, {
174
+ pixelRatio: 2,
175
+ backgroundColor: bgColor
176
+ });
177
+ const res = await fetch(dataUrl);
178
+ const blob = await res.blob();
179
+ const fullFilename = uuidFilename(filename, "png");
180
+ await saveBlob(blob, fullFilename, "image/png");
181
+ } finally {
182
+ element.classList.remove("code-block--exporting");
183
+ element.classList.remove("data-table--exporting");
184
+ element.style.borderRadius = origBorderRadius;
185
+ if (parent) {
186
+ if (nextSibling) {
187
+ parent.insertBefore(element, nextSibling);
188
+ } else {
189
+ parent.appendChild(element);
190
+ }
191
+ }
192
+ wrapper.remove();
193
+ }
194
+ }
195
+ var init_exportToImage = __esm({
196
+ "src/utils/exportToImage.ts"() {
197
+ "use strict";
198
+ init_saveFile();
199
+ }
200
+ });
201
+
202
+ // src/index.ts
203
+ var index_exports = {};
204
+ __export(index_exports, {
205
+ AreaChart: () => AreaChart,
206
+ BarChart: () => BarChart,
207
+ DataTable: () => DataTable,
208
+ DonutChart: () => DonutChart,
209
+ LineChart: () => LineChart,
210
+ PieChart: () => PieChart,
211
+ Widget: () => Widget,
212
+ saveBlob: () => saveBlob,
213
+ saveFile: () => saveFile,
214
+ uuidFilename: () => uuidFilename
215
+ });
216
+ module.exports = __toCommonJS(index_exports);
217
+
218
+ // src/Widget/Widget.tsx
219
+ var import_react = require("react");
220
+ var import_icons = require("@orchestra-mcp/icons");
221
+ var import_jsx_runtime = require("react/jsx-runtime");
222
+ var Widget = ({
223
+ title,
224
+ subtitle,
225
+ children,
226
+ icon,
227
+ actions,
228
+ collapsible = false,
229
+ defaultCollapsed = false,
230
+ loading = false,
231
+ error,
232
+ onRefresh,
233
+ size = "medium",
234
+ onContextMenu,
235
+ style
236
+ }) => {
237
+ const [collapsed, setCollapsed] = (0, import_react.useState)(defaultCollapsed);
238
+ const isCollapsed = collapsible && collapsed;
239
+ const headerClasses = [
240
+ "widget__header",
241
+ isCollapsed ? "widget__header--collapsed" : ""
242
+ ].filter(Boolean).join(" ");
243
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: `widget widget--${size}${isCollapsed ? " widget--collapsed" : ""}`, "data-testid": "widget", style, children: [
244
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: headerClasses, onContextMenu, children: [
245
+ icon && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "widget__icon", children: icon }),
246
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "widget__title-group", children: [
247
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "widget__title", children: title }),
248
+ subtitle && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "widget__subtitle", children: subtitle })
249
+ ] }),
250
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "widget__actions", children: [
251
+ actions?.map((action) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
252
+ "button",
253
+ {
254
+ className: "widget__action-btn",
255
+ onClick: action.onClick,
256
+ "aria-label": action.label,
257
+ title: action.label,
258
+ type: "button",
259
+ children: action.icon ?? action.label
260
+ },
261
+ action.id
262
+ )),
263
+ onRefresh && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
264
+ "button",
265
+ {
266
+ className: "widget__action-btn",
267
+ onClick: onRefresh,
268
+ "aria-label": "Refresh",
269
+ title: "Refresh",
270
+ type: "button",
271
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_icons.BoxIcon, { name: "bx-refresh", size: 14 })
272
+ }
273
+ ),
274
+ collapsible && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
275
+ "button",
276
+ {
277
+ className: `widget__collapse-btn${isCollapsed ? " widget__collapse-btn--collapsed" : ""}`,
278
+ onClick: () => setCollapsed((c) => !c),
279
+ "aria-label": isCollapsed ? "Expand" : "Collapse",
280
+ type: "button",
281
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_icons.BoxIcon, { name: "bx-chevron-down", size: 14 })
282
+ }
283
+ )
284
+ ] })
285
+ ] }),
286
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: `widget__body${isCollapsed ? " widget__body--hidden" : ""}`, children: [
287
+ error && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "widget__error", role: "alert", children: [
288
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "widget__error-text", children: error }),
289
+ onRefresh && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
290
+ "button",
291
+ {
292
+ className: "widget__retry-btn",
293
+ onClick: onRefresh,
294
+ type: "button",
295
+ children: "Retry"
296
+ }
297
+ )
298
+ ] }),
299
+ !error && children,
300
+ loading && !error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "widget__loading", "data-testid": "widget-loading", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "widget__shimmer" }) })
301
+ ] })
302
+ ] });
303
+ };
304
+
305
+ // src/DataTable/DataTable.tsx
306
+ var import_react2 = require("react");
307
+ init_saveFile();
308
+ var import_jsx_runtime2 = require("react/jsx-runtime");
309
+ function isNumeric(value) {
310
+ return value !== "" && !isNaN(Number(value));
311
+ }
312
+ function compareValues(a, b) {
313
+ if (isNumeric(a) && isNumeric(b)) {
314
+ return Number(a) - Number(b);
315
+ }
316
+ return a.localeCompare(b);
317
+ }
318
+ function escapeCsvValue(value) {
319
+ if (value.includes(",") || value.includes('"') || value.includes("\n")) {
320
+ return `"${value.replace(/"/g, '""')}"`;
321
+ }
322
+ return value;
323
+ }
324
+ var CopyIcon = () => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
325
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: "5", y: "5", width: "8", height: "8", rx: "1.5", stroke: "currentColor", strokeWidth: "1.5" }),
326
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M11 3H4.5A1.5 1.5 0 0 0 3 4.5V11", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" })
327
+ ] });
328
+ var CheckIcon = () => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M3.5 8.5L6.5 11.5L12.5 4.5", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) });
329
+ var DownloadIcon = () => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M8 2v8m0 0L5 7m3 3l3-3M3 12h10", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) });
330
+ var ImageIcon = () => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
331
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: "2", y: "2", width: "12", height: "12", rx: "2", stroke: "currentColor", strokeWidth: "1.5" }),
332
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "6", cy: "6", r: "1.5", fill: "currentColor" }),
333
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M2 11l3-3 2 2 3-3 4 4", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" })
334
+ ] });
335
+ var DataTable = ({
336
+ columns,
337
+ rows,
338
+ sortable = false,
339
+ exportable = false,
340
+ exportImage = false,
341
+ showHeader = false,
342
+ label,
343
+ renderCell,
344
+ onLinkClick,
345
+ maxHeight,
346
+ className
347
+ }) => {
348
+ const [sort, setSort] = (0, import_react2.useState)({ column: null, direction: "asc" });
349
+ const [copied, setCopied] = (0, import_react2.useState)(false);
350
+ const timerRef = (0, import_react2.useRef)(null);
351
+ const tableRef = (0, import_react2.useRef)(null);
352
+ const handleSort = (0, import_react2.useCallback)((col) => {
353
+ if (!sortable && !col.sortable) return;
354
+ setSort((prev) => {
355
+ if (prev.column === col.key) {
356
+ return prev.direction === "asc" ? { column: col.key, direction: "desc" } : { column: null, direction: "asc" };
357
+ }
358
+ return { column: col.key, direction: "asc" };
359
+ });
360
+ }, [sortable]);
361
+ const sortedRows = (0, import_react2.useMemo)(() => {
362
+ if (!sort.column) return rows;
363
+ const colIndex = columns.findIndex((c) => c.key === sort.column);
364
+ if (colIndex === -1) return rows;
365
+ const sorted = [...rows].sort((a, b) => compareValues(a[colIndex], b[colIndex]));
366
+ return sort.direction === "desc" ? sorted.reverse() : sorted;
367
+ }, [rows, columns, sort]);
368
+ const buildCsv = (0, import_react2.useCallback)(() => {
369
+ const headerLine = columns.map((c) => escapeCsvValue(c.header)).join(",");
370
+ const dataLines = rows.map((row) => row.map(escapeCsvValue).join(","));
371
+ return [headerLine, ...dataLines].join("\n");
372
+ }, [columns, rows]);
373
+ const handleExport = (0, import_react2.useCallback)(async () => {
374
+ await saveFile(buildCsv(), uuidFilename("table-export", "csv"), "text/csv");
375
+ }, [buildCsv]);
376
+ const handleCopy = (0, import_react2.useCallback)(() => {
377
+ const csv = buildCsv();
378
+ navigator.clipboard.writeText(csv).then(() => {
379
+ setCopied(true);
380
+ if (timerRef.current) clearTimeout(timerRef.current);
381
+ timerRef.current = setTimeout(() => setCopied(false), 2e3);
382
+ });
383
+ }, [buildCsv]);
384
+ const handleExportImage = (0, import_react2.useCallback)(async () => {
385
+ if (!tableRef.current) return;
386
+ const { exportToImage: exportToImage2 } = await Promise.resolve().then(() => (init_exportToImage(), exportToImage_exports));
387
+ await exportToImage2(tableRef.current, "table-export");
388
+ }, []);
389
+ const handleTableClick = (0, import_react2.useCallback)((e) => {
390
+ if (!onLinkClick) return;
391
+ const target = e.target;
392
+ const link = target.closest("[data-md-link]");
393
+ if (link) {
394
+ e.preventDefault();
395
+ onLinkClick(link.dataset.mdLink);
396
+ }
397
+ }, [onLinkClick]);
398
+ const isSortable = (col) => sortable || col.sortable === true;
399
+ const hasActions = exportable || exportImage || showHeader;
400
+ const containerStyle = maxHeight ? { maxHeight: `${maxHeight}px` } : void 0;
401
+ const containerClass = [
402
+ "data-table-container",
403
+ showHeader ? "data-table-container--card" : "",
404
+ maxHeight ? "data-table-container--scrollable" : "",
405
+ className ?? ""
406
+ ].filter(Boolean).join(" ");
407
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: containerClass, ref: tableRef, children: [
408
+ showHeader && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "data-table__header", children: [
409
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "data-table__header-left", children: [
410
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "data-table__dots", "aria-hidden": "true", children: [
411
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "data-table__dot data-table__dot--red" }),
412
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "data-table__dot data-table__dot--yellow" }),
413
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "data-table__dot data-table__dot--green" })
414
+ ] }),
415
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "data-table__badge", children: label || "Table" })
416
+ ] }),
417
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "data-table__actions", children: [
418
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
419
+ "button",
420
+ {
421
+ type: "button",
422
+ className: `data-table__btn ${copied ? "data-table__btn--copied" : ""}`,
423
+ onClick: handleCopy,
424
+ "aria-label": "Copy as CSV",
425
+ title: "Copy as CSV",
426
+ children: copied ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(CheckIcon, {}) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(CopyIcon, {})
427
+ }
428
+ ),
429
+ exportable && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
430
+ "button",
431
+ {
432
+ type: "button",
433
+ className: "data-table__btn",
434
+ onClick: handleExport,
435
+ "aria-label": "Download CSV",
436
+ title: "Download CSV",
437
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(DownloadIcon, {})
438
+ }
439
+ ),
440
+ exportImage && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
441
+ "button",
442
+ {
443
+ type: "button",
444
+ className: "data-table__btn",
445
+ onClick: handleExportImage,
446
+ "aria-label": "Export as image",
447
+ title: "Export as image",
448
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ImageIcon, {})
449
+ }
450
+ )
451
+ ] })
452
+ ] }),
453
+ !showHeader && (exportable || exportImage) && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "data-table__toolbar", children: [
454
+ exportable && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "data-table__export", onClick: handleExport, children: "Export CSV" }),
455
+ exportImage && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
456
+ "button",
457
+ {
458
+ type: "button",
459
+ className: "data-table__export",
460
+ onClick: handleExportImage,
461
+ "aria-label": "Export as image",
462
+ children: "Export Image"
463
+ }
464
+ )
465
+ ] }),
466
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "data-table__body", style: containerStyle, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("table", { className: "data-table", children: [
467
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("tr", { children: columns.map((col) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
468
+ "th",
469
+ {
470
+ className: [
471
+ `data-table__th--${col.align ?? "left"}`,
472
+ isSortable(col) ? "data-table__th--sortable" : ""
473
+ ].filter(Boolean).join(" "),
474
+ onClick: () => isSortable(col) && handleSort(col),
475
+ children: [
476
+ renderCell ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { dangerouslySetInnerHTML: { __html: renderCell(col.header) } }) : col.header,
477
+ isSortable(col) && sort.column === col.key && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "data-table__sort", children: sort.direction === "asc" ? "\u25B2" : "\u25BC" })
478
+ ]
479
+ },
480
+ col.key
481
+ )) }) }),
482
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("tbody", { onClick: handleTableClick, children: sortedRows.map((row, rowIdx) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("tr", { children: row.map(
483
+ (cell, cellIdx) => renderCell ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
484
+ "td",
485
+ {
486
+ className: `data-table__td--${columns[cellIdx]?.align ?? "left"}`,
487
+ dangerouslySetInnerHTML: { __html: renderCell(cell) }
488
+ },
489
+ cellIdx
490
+ ) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
491
+ "td",
492
+ {
493
+ className: `data-table__td--${columns[cellIdx]?.align ?? "left"}`,
494
+ children: cell
495
+ },
496
+ cellIdx
497
+ )
498
+ ) }, rowIdx)) })
499
+ ] }) })
500
+ ] });
501
+ };
502
+
503
+ // src/Charts/LineChart.tsx
504
+ var import_jsx_runtime3 = require("react/jsx-runtime");
505
+ var COLORS = ["#3b82f6", "#22c55e", "#f59e0b", "#ef4444", "#8b5cf6", "#06b6d4", "#ec4899", "#f97316"];
506
+ var LineChart = ({
507
+ data,
508
+ width = 400,
509
+ height = 300,
510
+ showLegend = false,
511
+ title,
512
+ className
513
+ }) => {
514
+ if (!data.length) {
515
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: `chart__empty ${className ?? ""}`, style: { width, height }, children: "No data" });
516
+ }
517
+ const pad = { top: 20, right: 20, bottom: 40, left: 50 };
518
+ const w = width - pad.left - pad.right;
519
+ const h = height - pad.top - pad.bottom;
520
+ const max = Math.max(...data.map((d) => d.value));
521
+ const step = w / Math.max(data.length - 1, 1);
522
+ const points = data.map((d, i) => ({
523
+ x: pad.left + i * step,
524
+ y: pad.top + h - d.value / (max || 1) * h
525
+ }));
526
+ const pathD = points.map((p, i) => `${i === 0 ? "M" : "L"}${p.x},${p.y}`).join(" ");
527
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: `chart ${className ?? ""}`, children: [
528
+ title && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "chart__title", children: title }),
529
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("svg", { className: "chart__svg", width, height, children: [
530
+ [0, 0.25, 0.5, 0.75, 1].map((t) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
531
+ "line",
532
+ {
533
+ className: "chart__grid-line",
534
+ x1: pad.left,
535
+ x2: width - pad.right,
536
+ y1: pad.top + h * (1 - t),
537
+ y2: pad.top + h * (1 - t)
538
+ },
539
+ t
540
+ )),
541
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { className: "chart__line", d: pathD, stroke: COLORS[0] }),
542
+ points.map((p, i) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
543
+ "circle",
544
+ {
545
+ className: "chart__dot",
546
+ cx: p.x,
547
+ cy: p.y,
548
+ r: 4,
549
+ fill: data[i].color ?? COLORS[0]
550
+ },
551
+ i
552
+ )),
553
+ data.map((d, i) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
554
+ "text",
555
+ {
556
+ className: "chart__axis-label",
557
+ x: points[i].x,
558
+ y: height - 8,
559
+ textAnchor: "middle",
560
+ children: d.label
561
+ },
562
+ i
563
+ ))
564
+ ] }),
565
+ showLegend && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "chart__legend", children: data.map((d, i) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "chart__legend-item", children: [
566
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "chart__legend-dot", style: { background: d.color ?? COLORS[i % COLORS.length] } }),
567
+ d.label
568
+ ] }, i)) })
569
+ ] });
570
+ };
571
+
572
+ // src/Charts/BarChart.tsx
573
+ var import_jsx_runtime4 = require("react/jsx-runtime");
574
+ var COLORS2 = ["#3b82f6", "#22c55e", "#f59e0b", "#ef4444", "#8b5cf6", "#06b6d4", "#ec4899", "#f97316"];
575
+ var BarChart = ({
576
+ data,
577
+ width = 400,
578
+ height = 300,
579
+ showLegend = false,
580
+ title,
581
+ className
582
+ }) => {
583
+ if (!data.length) {
584
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: `chart__empty ${className ?? ""}`, style: { width, height }, children: "No data" });
585
+ }
586
+ const pad = { top: 20, right: 20, bottom: 40, left: 50 };
587
+ const w = width - pad.left - pad.right;
588
+ const h = height - pad.top - pad.bottom;
589
+ const max = Math.max(...data.map((d) => d.value));
590
+ const barW = w / data.length * 0.7;
591
+ const gap = w / data.length * 0.3;
592
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: `chart ${className ?? ""}`, children: [
593
+ title && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "chart__title", children: title }),
594
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("svg", { className: "chart__svg", width, height, children: [
595
+ [0, 0.25, 0.5, 0.75, 1].map((t) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
596
+ "line",
597
+ {
598
+ className: "chart__grid-line",
599
+ x1: pad.left,
600
+ x2: width - pad.right,
601
+ y1: pad.top + h * (1 - t),
602
+ y2: pad.top + h * (1 - t)
603
+ },
604
+ t
605
+ )),
606
+ data.map((d, i) => {
607
+ const barH = d.value / (max || 1) * h;
608
+ const x = pad.left + i * (barW + gap) + gap / 2;
609
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("g", { children: [
610
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
611
+ "rect",
612
+ {
613
+ className: "chart__bar",
614
+ x,
615
+ y: pad.top + h - barH,
616
+ width: barW,
617
+ height: barH,
618
+ rx: 3,
619
+ fill: d.color ?? COLORS2[i % COLORS2.length]
620
+ }
621
+ ),
622
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("text", { className: "chart__axis-label", x: x + barW / 2, y: height - 8, textAnchor: "middle", children: d.label })
623
+ ] }, i);
624
+ })
625
+ ] }),
626
+ showLegend && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "chart__legend", children: data.map((d, i) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: "chart__legend-item", children: [
627
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "chart__legend-dot", style: { background: d.color ?? COLORS2[i % COLORS2.length] } }),
628
+ d.label
629
+ ] }, i)) })
630
+ ] });
631
+ };
632
+
633
+ // src/Charts/PieChart.tsx
634
+ var import_jsx_runtime5 = require("react/jsx-runtime");
635
+ var COLORS3 = ["#3b82f6", "#22c55e", "#f59e0b", "#ef4444", "#8b5cf6", "#06b6d4", "#ec4899", "#f97316"];
636
+ function polarToCartesian(cx, cy, r, angle) {
637
+ const rad = (angle - 90) * Math.PI / 180;
638
+ return { x: cx + r * Math.cos(rad), y: cy + r * Math.sin(rad) };
639
+ }
640
+ function arcPath(cx, cy, r, startAngle, endAngle) {
641
+ const start = polarToCartesian(cx, cy, r, endAngle);
642
+ const end = polarToCartesian(cx, cy, r, startAngle);
643
+ const large = endAngle - startAngle > 180 ? 1 : 0;
644
+ return `M ${start.x} ${start.y} A ${r} ${r} 0 ${large} 0 ${end.x} ${end.y} L ${cx} ${cy} Z`;
645
+ }
646
+ var PieChart = ({
647
+ data,
648
+ width = 300,
649
+ height = 300,
650
+ showLegend = true,
651
+ title,
652
+ className
653
+ }) => {
654
+ if (!data.length) {
655
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: `chart__empty ${className ?? ""}`, style: { width, height }, children: "No data" });
656
+ }
657
+ const total = data.reduce((sum, d) => sum + d.value, 0);
658
+ const cx = width / 2;
659
+ const cy = height / 2;
660
+ const r = Math.min(cx, cy) - 10;
661
+ let startAngle = 0;
662
+ const slices = data.map((d, i) => {
663
+ const sweep = total > 0 ? d.value / total * 360 : 0;
664
+ const path = arcPath(cx, cy, r, startAngle, startAngle + sweep);
665
+ startAngle += sweep;
666
+ return { path, color: d.color ?? COLORS3[i % COLORS3.length], label: d.label };
667
+ });
668
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: `chart ${className ?? ""}`, children: [
669
+ title && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "chart__title", children: title }),
670
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("svg", { className: "chart__svg", width, height, children: slices.map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { className: "chart__slice", d: s.path, fill: s.color }, i)) }),
671
+ showLegend && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "chart__legend", children: data.map((d, i) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "chart__legend-item", children: [
672
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "chart__legend-dot", style: { background: d.color ?? COLORS3[i % COLORS3.length] } }),
673
+ d.label,
674
+ ": ",
675
+ d.value
676
+ ] }, i)) })
677
+ ] });
678
+ };
679
+
680
+ // src/Charts/AreaChart.tsx
681
+ var import_jsx_runtime6 = require("react/jsx-runtime");
682
+ var COLORS4 = ["#3b82f6", "#22c55e", "#f59e0b", "#ef4444", "#8b5cf6", "#06b6d4", "#ec4899", "#f97316"];
683
+ var AreaChart = ({
684
+ data,
685
+ width = 400,
686
+ height = 300,
687
+ showLegend = false,
688
+ title,
689
+ className
690
+ }) => {
691
+ if (!data.length) {
692
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: `chart__empty ${className ?? ""}`, style: { width, height }, children: "No data" });
693
+ }
694
+ const pad = { top: 20, right: 20, bottom: 40, left: 50 };
695
+ const w = width - pad.left - pad.right;
696
+ const h = height - pad.top - pad.bottom;
697
+ const max = Math.max(...data.map((d) => d.value));
698
+ const step = w / Math.max(data.length - 1, 1);
699
+ const points = data.map((d, i) => ({
700
+ x: pad.left + i * step,
701
+ y: pad.top + h - d.value / (max || 1) * h
702
+ }));
703
+ const lineD = points.map((p, i) => `${i === 0 ? "M" : "L"}${p.x},${p.y}`).join(" ");
704
+ const areaD = `${lineD} L${points[points.length - 1].x},${pad.top + h} L${points[0].x},${pad.top + h} Z`;
705
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: `chart ${className ?? ""}`, children: [
706
+ title && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "chart__title", children: title }),
707
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("svg", { className: "chart__svg", width, height, children: [
708
+ [0, 0.25, 0.5, 0.75, 1].map((t) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
709
+ "line",
710
+ {
711
+ className: "chart__grid-line",
712
+ x1: pad.left,
713
+ x2: width - pad.right,
714
+ y1: pad.top + h * (1 - t),
715
+ y2: pad.top + h * (1 - t)
716
+ },
717
+ t
718
+ )),
719
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { className: "chart__area", d: areaD, fill: COLORS4[0] }),
720
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { className: "chart__line", d: lineD, stroke: COLORS4[0] }),
721
+ points.map((p, i) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
722
+ "circle",
723
+ {
724
+ className: "chart__dot",
725
+ cx: p.x,
726
+ cy: p.y,
727
+ r: 4,
728
+ fill: data[i].color ?? COLORS4[0]
729
+ },
730
+ i
731
+ )),
732
+ data.map((d, i) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("text", { className: "chart__axis-label", x: points[i].x, y: height - 8, textAnchor: "middle", children: d.label }, i))
733
+ ] }),
734
+ showLegend && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "chart__legend", children: data.map((d, i) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "chart__legend-item", children: [
735
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "chart__legend-dot", style: { background: d.color ?? COLORS4[i % COLORS4.length] } }),
736
+ d.label
737
+ ] }, i)) })
738
+ ] });
739
+ };
740
+
741
+ // src/Charts/DonutChart.tsx
742
+ var import_jsx_runtime7 = require("react/jsx-runtime");
743
+ var COLORS5 = ["#3b82f6", "#22c55e", "#f59e0b", "#ef4444", "#8b5cf6", "#06b6d4", "#ec4899", "#f97316"];
744
+ function polarToCartesian2(cx, cy, r, angle) {
745
+ const rad = (angle - 90) * Math.PI / 180;
746
+ return { x: cx + r * Math.cos(rad), y: cy + r * Math.sin(rad) };
747
+ }
748
+ function donutArc(cx, cy, outer, inner, start, end) {
749
+ const oStart = polarToCartesian2(cx, cy, outer, end);
750
+ const oEnd = polarToCartesian2(cx, cy, outer, start);
751
+ const iStart = polarToCartesian2(cx, cy, inner, start);
752
+ const iEnd = polarToCartesian2(cx, cy, inner, end);
753
+ const large = end - start > 180 ? 1 : 0;
754
+ return [
755
+ `M ${oStart.x} ${oStart.y}`,
756
+ `A ${outer} ${outer} 0 ${large} 0 ${oEnd.x} ${oEnd.y}`,
757
+ `L ${iStart.x} ${iStart.y}`,
758
+ `A ${inner} ${inner} 0 ${large} 1 ${iEnd.x} ${iEnd.y}`,
759
+ "Z"
760
+ ].join(" ");
761
+ }
762
+ var DonutChart = ({
763
+ data,
764
+ width = 300,
765
+ height = 300,
766
+ showLegend = true,
767
+ title,
768
+ className,
769
+ innerRatio = 0.6,
770
+ centerLabel
771
+ }) => {
772
+ if (!data.length) {
773
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: `chart__empty ${className ?? ""}`, style: { width, height }, children: "No data" });
774
+ }
775
+ const total = data.reduce((sum, d) => sum + d.value, 0);
776
+ const cx = width / 2;
777
+ const cy = height / 2;
778
+ const outer = Math.min(cx, cy) - 10;
779
+ const inner = outer * innerRatio;
780
+ let startAngle = 0;
781
+ const slices = data.map((d, i) => {
782
+ const sweep = total > 0 ? d.value / total * 360 : 0;
783
+ const path = donutArc(cx, cy, outer, inner, startAngle, startAngle + sweep);
784
+ startAngle += sweep;
785
+ return { path, color: d.color ?? COLORS5[i % COLORS5.length] };
786
+ });
787
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: `chart ${className ?? ""}`, children: [
788
+ title && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "chart__title", children: title }),
789
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("svg", { className: "chart__svg", width, height, children: [
790
+ slices.map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("path", { className: "chart__slice", d: s.path, fill: s.color }, i)),
791
+ centerLabel && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("text", { className: "chart__donut-label", x: cx, y: cy, children: centerLabel })
792
+ ] }),
793
+ showLegend && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "chart__legend", children: data.map((d, i) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "chart__legend-item", children: [
794
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "chart__legend-dot", style: { background: d.color ?? COLORS5[i % COLORS5.length] } }),
795
+ d.label,
796
+ ": ",
797
+ d.value
798
+ ] }, i)) })
799
+ ] });
800
+ };
801
+
802
+ // src/index.ts
803
+ init_saveFile();
804
+ // Annotate the CommonJS export names for ESM import in node:
805
+ 0 && (module.exports = {
806
+ AreaChart,
807
+ BarChart,
808
+ DataTable,
809
+ DonutChart,
810
+ LineChart,
811
+ PieChart,
812
+ Widget,
813
+ saveBlob,
814
+ saveFile,
815
+ uuidFilename
816
+ });