@shapesos/clay 0.11.0 → 0.12.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/README.md +60 -0
- package/dist/artifacts.cjs +858 -132
- package/dist/artifacts.cjs.map +1 -1
- package/dist/artifacts.d.cts +38 -5
- package/dist/artifacts.d.ts +38 -5
- package/dist/artifacts.js +13 -3
- package/dist/blocks.cjs +1185 -467
- package/dist/blocks.cjs.map +1 -1
- package/dist/blocks.css +1 -1
- package/dist/blocks.d.cts +23 -10
- package/dist/blocks.d.ts +23 -10
- package/dist/blocks.js +6 -3
- package/dist/chart.cjs +594 -0
- package/dist/chart.cjs.map +1 -0
- package/dist/chart.d.cts +439 -0
- package/dist/chart.d.ts +439 -0
- package/dist/chart.js +32 -0
- package/dist/chat.cjs +1207 -489
- package/dist/chat.cjs.map +1 -1
- package/dist/chat.d.cts +3 -2
- package/dist/chat.d.ts +3 -2
- package/dist/chat.js +7 -4
- package/dist/{chunk-MXOPG747.js → chunk-36CB624W.js} +7 -7
- package/dist/chunk-36CB624W.js.map +1 -0
- package/dist/{chunk-L35M3OD5.js → chunk-4MZZH3WX.js} +5 -11
- package/dist/chunk-4MZZH3WX.js.map +1 -0
- package/dist/chunk-AQEJRMRN.js +1 -0
- package/dist/chunk-AQEJRMRN.js.map +1 -0
- package/dist/{chunk-OUW6PUEB.js → chunk-FFX3CAOX.js} +7 -7
- package/dist/chunk-P6GUNIAE.js +11 -0
- package/dist/chunk-P6GUNIAE.js.map +1 -0
- package/dist/chunk-QXGYMDIA.js +477 -0
- package/dist/chunk-QXGYMDIA.js.map +1 -0
- package/dist/{chunk-BX5TCEPR.js → chunk-Z5JWF24N.js} +388 -105
- package/dist/chunk-Z5JWF24N.js.map +1 -0
- package/dist/index.cjs +924 -198
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -3
- package/dist/index.d.ts +4 -3
- package/dist/index.js +23 -15
- package/dist/table.js +2 -1
- package/dist/types-B2aYk82c.d.cts +29 -0
- package/dist/types-B2aYk82c.d.ts +29 -0
- package/dist/types-C0BvwliI.d.cts +332 -0
- package/dist/types-C5bFH4v3.d.ts +332 -0
- package/dist/{types-DuuRI4ll.d.cts → types-DCutaXjZ.d.cts} +16 -1
- package/dist/{types-C9XX-Uhk.d.ts → types-uPfn67Dc.d.ts} +16 -1
- package/package.json +13 -1
- package/dist/chunk-BX5TCEPR.js.map +0 -1
- package/dist/chunk-L35M3OD5.js.map +0 -1
- package/dist/chunk-MEJESPTZ.js +0 -1
- package/dist/chunk-MXOPG747.js.map +0 -1
- package/dist/types-3Gzk7cRt.d.cts +0 -121
- package/dist/types-3Gzk7cRt.d.ts +0 -121
- /package/dist/{chunk-MEJESPTZ.js.map → chart.js.map} +0 -0
- /package/dist/{chunk-OUW6PUEB.js.map → chunk-FFX3CAOX.js.map} +0 -0
package/dist/artifacts.cjs
CHANGED
|
@@ -32,6 +32,9 @@ var artifacts_exports = {};
|
|
|
32
32
|
__export(artifacts_exports, {
|
|
33
33
|
ArtifactDownloadButton: () => ArtifactDownloadButton,
|
|
34
34
|
ArtifactServices: () => ArtifactServices,
|
|
35
|
+
ChartArtifact: () => ChartArtifact,
|
|
36
|
+
DEFAULT_CHART_ARTIFACT_LABELS: () => DEFAULT_CHART_ARTIFACT_LABELS,
|
|
37
|
+
DEFAULT_TABLE_ARTIFACT_LABELS: () => DEFAULT_TABLE_ARTIFACT_LABELS,
|
|
35
38
|
TableArtifact: () => TableArtifact,
|
|
36
39
|
artifactToClipboardText: () => artifactToClipboardText,
|
|
37
40
|
artifactTypes: () => artifactTypes,
|
|
@@ -39,13 +42,33 @@ __export(artifacts_exports, {
|
|
|
39
42
|
});
|
|
40
43
|
module.exports = __toCommonJS(artifacts_exports);
|
|
41
44
|
|
|
42
|
-
// src/components/artifacts/
|
|
45
|
+
// src/components/artifacts/artifact-base.ts
|
|
43
46
|
var artifactTypes = {
|
|
44
|
-
TABLE: "TABLE"
|
|
47
|
+
TABLE: "TABLE",
|
|
48
|
+
CHART: "CHART"
|
|
45
49
|
};
|
|
46
50
|
|
|
47
|
-
// src/components/artifacts/
|
|
48
|
-
var
|
|
51
|
+
// src/components/artifacts/chart-artifact/types.ts
|
|
52
|
+
var DEFAULT_CHART_ARTIFACT_LABELS = {
|
|
53
|
+
download: "Download CSV",
|
|
54
|
+
unavailable: "Chart data is unavailable.",
|
|
55
|
+
loadError: "Couldn't load chart data.",
|
|
56
|
+
loading: "Loading chart\u2026",
|
|
57
|
+
empty: "No data to chart.",
|
|
58
|
+
othersCategoryLabels: ["Others", "Other"]
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
// src/components/artifacts/table-artifact/types.ts
|
|
62
|
+
var DEFAULT_TABLE_ARTIFACT_LABELS = {
|
|
63
|
+
download: "Download CSV",
|
|
64
|
+
unavailable: "Table data is unavailable.",
|
|
65
|
+
loadError: "Couldn't load table data.",
|
|
66
|
+
loading: "Loading table\u2026",
|
|
67
|
+
empty: "No rows to display."
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
// src/components/artifacts/chart-artifact/chart-artifact.tsx
|
|
71
|
+
var import_react9 = require("react");
|
|
49
72
|
|
|
50
73
|
// src/lib/utils.ts
|
|
51
74
|
var import_clsx = require("clsx");
|
|
@@ -369,7 +392,12 @@ TooltipContent.displayName = TooltipPrimitive.Content.displayName;
|
|
|
369
392
|
|
|
370
393
|
// src/components/artifacts/artifact-download-button/artifact-download-button.tsx
|
|
371
394
|
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
372
|
-
|
|
395
|
+
var DEFAULT_LABEL = "Download CSV";
|
|
396
|
+
function ArtifactDownloadButton({
|
|
397
|
+
href,
|
|
398
|
+
label = DEFAULT_LABEL,
|
|
399
|
+
onClick
|
|
400
|
+
}) {
|
|
373
401
|
if (!isSafeDownloadUrl(href)) return null;
|
|
374
402
|
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(TooltipProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Tooltip, { children: [
|
|
375
403
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
@@ -379,12 +407,13 @@ function ArtifactDownloadButton({ href }) {
|
|
|
379
407
|
href,
|
|
380
408
|
download: true,
|
|
381
409
|
rel: "noopener noreferrer",
|
|
410
|
+
onClick,
|
|
382
411
|
icon: IconDownload,
|
|
383
412
|
size: "small",
|
|
384
|
-
"aria-label":
|
|
413
|
+
"aria-label": label
|
|
385
414
|
}
|
|
386
415
|
) }),
|
|
387
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(TooltipContent, { children:
|
|
416
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(TooltipContent, { children: label })
|
|
388
417
|
] }) });
|
|
389
418
|
}
|
|
390
419
|
function isSafeDownloadUrl(url) {
|
|
@@ -396,25 +425,778 @@ function isSafeDownloadUrl(url) {
|
|
|
396
425
|
}
|
|
397
426
|
}
|
|
398
427
|
|
|
428
|
+
// src/components/artifacts/status-message.tsx
|
|
429
|
+
var import_react4 = require("react");
|
|
430
|
+
function StatusMessage({ $tone, children }) {
|
|
431
|
+
return (0, import_react4.createElement)(
|
|
432
|
+
"div",
|
|
433
|
+
{
|
|
434
|
+
className: `py-6 px-2 text-center text-sm font-medium leading-5 ${$tone === "error" ? "text-red-600" : "text-brown-70"}`
|
|
435
|
+
},
|
|
436
|
+
children
|
|
437
|
+
);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
// src/components/chart/bar-chart/bar-chart.tsx
|
|
441
|
+
var import_react5 = require("react");
|
|
442
|
+
var import_recharts2 = require("recharts");
|
|
443
|
+
|
|
444
|
+
// src/components/chart/chart-container.tsx
|
|
445
|
+
var import_recharts = require("recharts");
|
|
446
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
447
|
+
function ChartContainer({
|
|
448
|
+
children,
|
|
449
|
+
height = 320,
|
|
450
|
+
width = "100%",
|
|
451
|
+
className,
|
|
452
|
+
style
|
|
453
|
+
}) {
|
|
454
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: cn("text-xs text-foreground", className), style: { width, height, ...style }, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_recharts.ResponsiveContainer, { width: "100%", height: "100%", children }) });
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
// src/components/chart/chart-legend-content.tsx
|
|
458
|
+
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
459
|
+
function ChartLegendContent({ payload, className, iconType = "square" }) {
|
|
460
|
+
if (!payload || payload.length === 0) return null;
|
|
461
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
462
|
+
"ul",
|
|
463
|
+
{
|
|
464
|
+
className: cn("flex flex-wrap items-center justify-center gap-x-4 gap-y-2 text-xs text-brown-80", className),
|
|
465
|
+
children: payload.map((entry, i) => {
|
|
466
|
+
const swatchColor = typeof entry.color === "string" ? entry.color : "transparent";
|
|
467
|
+
const label = entry.value !== void 0 ? String(entry.value) : "";
|
|
468
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("li", { className: "flex items-center gap-1.5", children: [
|
|
469
|
+
iconType === "square" ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "inline-block h-2.5 w-2.5 shrink-0 rounded-sm", style: { background: swatchColor } }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "inline-block h-[3px] w-4 shrink-0 rounded-full", style: { background: swatchColor } }),
|
|
470
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: label })
|
|
471
|
+
] }, `${label}-${i}`);
|
|
472
|
+
})
|
|
473
|
+
}
|
|
474
|
+
);
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
// src/components/chart/chart-tooltip-content.tsx
|
|
478
|
+
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
479
|
+
function ChartTooltipContent({
|
|
480
|
+
active,
|
|
481
|
+
payload,
|
|
482
|
+
label,
|
|
483
|
+
className,
|
|
484
|
+
showLabel = true,
|
|
485
|
+
formatValue
|
|
486
|
+
}) {
|
|
487
|
+
if (!active || !payload || payload.length === 0) return null;
|
|
488
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
489
|
+
"div",
|
|
490
|
+
{
|
|
491
|
+
className: cn(
|
|
492
|
+
"rounded-lg border border-brown-40 bg-brown-10 px-3 py-2 text-xs shadow-sm",
|
|
493
|
+
"min-w-32 text-foreground",
|
|
494
|
+
className
|
|
495
|
+
),
|
|
496
|
+
children: [
|
|
497
|
+
showLabel && label !== void 0 && label !== null ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "mb-1.5 font-medium text-brown-90", children: String(label) }) : null,
|
|
498
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("ul", { className: "space-y-1", children: payload.map((entry, i) => {
|
|
499
|
+
const name = entry.name !== void 0 ? String(entry.name) : "";
|
|
500
|
+
const rawValue = entry.value;
|
|
501
|
+
const display = formatValue ? formatValue(rawValue, name) : rawValue === void 0 ? "" : String(rawValue);
|
|
502
|
+
const swatch = typeof entry.color === "string" ? entry.color : "transparent";
|
|
503
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("li", { className: "flex items-center gap-2", children: [
|
|
504
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "inline-block h-2.5 w-2.5 shrink-0 rounded-sm", style: { background: swatch } }),
|
|
505
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "text-brown-70", children: name }),
|
|
506
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "ml-auto font-semibold tabular-nums text-brown-90", children: display })
|
|
507
|
+
] }, `${name}-${i}`);
|
|
508
|
+
}) })
|
|
509
|
+
]
|
|
510
|
+
}
|
|
511
|
+
);
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
// src/components/chart/chart-palette.ts
|
|
515
|
+
var CHART_PALETTE = [
|
|
516
|
+
colors["blue-600"],
|
|
517
|
+
// #2563EB — primary
|
|
518
|
+
colors["blue-400"],
|
|
519
|
+
// #60A5FA — mid
|
|
520
|
+
colors["blue-300"],
|
|
521
|
+
// #93C5FD — light
|
|
522
|
+
colors["blue-100"],
|
|
523
|
+
// #DBEAFE — very light
|
|
524
|
+
colors["indigo-400"],
|
|
525
|
+
// #818CF8
|
|
526
|
+
colors["indigo-500"],
|
|
527
|
+
// #6366F1
|
|
528
|
+
colors["indigo-600"],
|
|
529
|
+
// #4F46E5
|
|
530
|
+
colors["indigo-800"],
|
|
531
|
+
// #3730A3
|
|
532
|
+
colors["blue-800"]
|
|
533
|
+
// #1E40AF — deepest
|
|
534
|
+
];
|
|
535
|
+
var OTHERS_SLICE_COLOR = colors["brown-60"];
|
|
536
|
+
var DEFAULT_OTHERS_CATEGORY_LABELS = ["others", "other"];
|
|
537
|
+
function isOthersCategory(value, labels) {
|
|
538
|
+
if (value === null || value === void 0) return false;
|
|
539
|
+
if (labels.length === 0) return false;
|
|
540
|
+
const normalized = String(value).trim().toLowerCase();
|
|
541
|
+
for (const label of labels) {
|
|
542
|
+
if (normalized === label.toLowerCase()) return true;
|
|
543
|
+
}
|
|
544
|
+
return false;
|
|
545
|
+
}
|
|
546
|
+
function colorForSeriesIndex(index, palette = CHART_PALETTE) {
|
|
547
|
+
return palette[index % palette.length] ?? CHART_PALETTE[0];
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
// src/components/chart/chart-styles.ts
|
|
551
|
+
var GRID_STROKE = colors["brown-40"];
|
|
552
|
+
var AXIS_STROKE = colors["brown-50"];
|
|
553
|
+
var AXIS_TICK_STYLE = {
|
|
554
|
+
fill: colors["brown-70"],
|
|
555
|
+
fontSize: 12,
|
|
556
|
+
fontWeight: 500
|
|
557
|
+
};
|
|
558
|
+
var CHART_VALUE_LABEL_STYLE = {
|
|
559
|
+
fill: colors["brown-70"],
|
|
560
|
+
fontSize: 12,
|
|
561
|
+
fontWeight: 500
|
|
562
|
+
};
|
|
563
|
+
var BAR_TOOLTIP_CURSOR_FILL = colors["brown-alpha-12"];
|
|
564
|
+
var LINE_ACTIVE_DOT_STROKE = colors["brown-10"];
|
|
565
|
+
var CHART_MARGIN = { top: 8, right: 16, left: 0, bottom: 4 };
|
|
566
|
+
var PIE_CHART_MARGIN = { top: 0, right: 24, left: 24, bottom: 4 };
|
|
567
|
+
var CHART_AXIS_PROPS = {
|
|
568
|
+
stroke: AXIS_STROKE,
|
|
569
|
+
tick: AXIS_TICK_STYLE,
|
|
570
|
+
tickLine: false,
|
|
571
|
+
axisLine: false
|
|
572
|
+
};
|
|
573
|
+
var CHART_GRID_PROPS = {
|
|
574
|
+
stroke: GRID_STROKE,
|
|
575
|
+
strokeDasharray: "3 3",
|
|
576
|
+
vertical: false
|
|
577
|
+
};
|
|
578
|
+
|
|
579
|
+
// src/components/chart/chart-tick-utils.ts
|
|
580
|
+
function maxTickCharsByCategoryCount(n) {
|
|
581
|
+
if (n <= 5) return 24;
|
|
582
|
+
if (n <= 8) return 14;
|
|
583
|
+
if (n <= 12) return 10;
|
|
584
|
+
if (n <= 16) return 7;
|
|
585
|
+
return 5;
|
|
586
|
+
}
|
|
587
|
+
function truncateLabel(value, maxChars) {
|
|
588
|
+
const s = String(value ?? "");
|
|
589
|
+
if (s.length <= maxChars) return s;
|
|
590
|
+
return s.slice(0, Math.max(1, maxChars - 1)).trimEnd() + "\u2026";
|
|
591
|
+
}
|
|
592
|
+
function evenlyDistributedVisibleIndices(n, target) {
|
|
593
|
+
if (n <= target) {
|
|
594
|
+
return new Set(Array.from({ length: n }, (_, i) => i));
|
|
595
|
+
}
|
|
596
|
+
const set = /* @__PURE__ */ new Set();
|
|
597
|
+
const step = (n - 1) / (target - 1);
|
|
598
|
+
for (let i = 0; i < target; i++) {
|
|
599
|
+
set.add(Math.round(i * step));
|
|
600
|
+
}
|
|
601
|
+
return set;
|
|
602
|
+
}
|
|
603
|
+
function defaultBarTickFormatter(categoryCount) {
|
|
604
|
+
const max = maxTickCharsByCategoryCount(categoryCount);
|
|
605
|
+
return (value) => truncateLabel(value, max);
|
|
606
|
+
}
|
|
607
|
+
function defaultLineChartTickFormatter(categoryCount, visibleIndices) {
|
|
608
|
+
const max = maxTickCharsByCategoryCount(visibleIndices.size || categoryCount);
|
|
609
|
+
return (value, index) => {
|
|
610
|
+
if (!visibleIndices.has(index)) return "";
|
|
611
|
+
return truncateLabel(value, max);
|
|
612
|
+
};
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
// src/components/chart/coerce-numeric.ts
|
|
616
|
+
function coerceNumericColumns(rows, numericKeys) {
|
|
617
|
+
return rows.map((row) => {
|
|
618
|
+
const out = { ...row };
|
|
619
|
+
for (const k of numericKeys) {
|
|
620
|
+
const v = out[k];
|
|
621
|
+
if (v == null) continue;
|
|
622
|
+
const n = Number(v);
|
|
623
|
+
out[k] = Number.isFinite(n) ? n : NaN;
|
|
624
|
+
}
|
|
625
|
+
return out;
|
|
626
|
+
});
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
// src/components/chart/bar-chart/bar-chart.tsx
|
|
630
|
+
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
631
|
+
function BarChart({
|
|
632
|
+
data,
|
|
633
|
+
xKey,
|
|
634
|
+
series,
|
|
635
|
+
stacked = false,
|
|
636
|
+
height = 320,
|
|
637
|
+
width = "100%",
|
|
638
|
+
className,
|
|
639
|
+
palette = CHART_PALETTE,
|
|
640
|
+
showTooltip = true,
|
|
641
|
+
showLegend,
|
|
642
|
+
showGrid = true,
|
|
643
|
+
showXAxis = true,
|
|
644
|
+
showYAxis = true,
|
|
645
|
+
showValueLabels,
|
|
646
|
+
xAxisInterval = 0,
|
|
647
|
+
xAxisAngle,
|
|
648
|
+
xAxisTickFormatter
|
|
649
|
+
}) {
|
|
650
|
+
const seriesKeys = (0, import_react5.useMemo)(() => series.map((s) => s.key), [series]);
|
|
651
|
+
const coercedData = (0, import_react5.useMemo)(
|
|
652
|
+
() => coerceNumericColumns(data, seriesKeys),
|
|
653
|
+
[data, seriesKeys]
|
|
654
|
+
);
|
|
655
|
+
const resolvedShowLegend = showLegend ?? series.length > 1;
|
|
656
|
+
const resolvedShowValueLabels = showValueLabels ?? (!stacked && series.length === 1);
|
|
657
|
+
const resolvedAngle = xAxisAngle ?? (coercedData.length >= 8 ? -30 : 0);
|
|
658
|
+
const resolvedTickFormatter = (0, import_react5.useMemo)(
|
|
659
|
+
() => xAxisTickFormatter ?? defaultBarTickFormatter(coercedData.length),
|
|
660
|
+
[xAxisTickFormatter, coercedData.length]
|
|
661
|
+
);
|
|
662
|
+
const angledLabelPad = resolvedAngle === 0 ? 0 : Math.min(Math.abs(resolvedAngle) * 0.7, 40);
|
|
663
|
+
const chartMargin = { ...CHART_MARGIN, bottom: CHART_MARGIN.bottom + angledLabelPad };
|
|
664
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ChartContainer, { height, width, className, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_recharts2.BarChart, { data: coercedData, margin: chartMargin, barCategoryGap: "22%", children: [
|
|
665
|
+
showGrid ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_recharts2.CartesianGrid, { ...CHART_GRID_PROPS }) : null,
|
|
666
|
+
showXAxis ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
667
|
+
import_recharts2.XAxis,
|
|
668
|
+
{
|
|
669
|
+
dataKey: xKey,
|
|
670
|
+
...CHART_AXIS_PROPS,
|
|
671
|
+
interval: xAxisInterval,
|
|
672
|
+
angle: resolvedAngle,
|
|
673
|
+
textAnchor: resolvedAngle !== 0 ? "end" : "middle",
|
|
674
|
+
tickMargin: resolvedAngle === 0 ? 8 : 12,
|
|
675
|
+
height: resolvedAngle !== 0 ? 60 : void 0,
|
|
676
|
+
tickFormatter: resolvedTickFormatter
|
|
677
|
+
}
|
|
678
|
+
) : null,
|
|
679
|
+
showYAxis ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_recharts2.YAxis, { ...CHART_AXIS_PROPS, width: 40 }) : null,
|
|
680
|
+
showTooltip ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_recharts2.Tooltip, { cursor: { fill: BAR_TOOLTIP_CURSOR_FILL }, content: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ChartTooltipContent, {}) }) : null,
|
|
681
|
+
resolvedShowLegend ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
682
|
+
import_recharts2.Legend,
|
|
683
|
+
{
|
|
684
|
+
verticalAlign: "top",
|
|
685
|
+
align: "center",
|
|
686
|
+
wrapperStyle: { top: 0 },
|
|
687
|
+
itemSorter: null,
|
|
688
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ChartLegendContent, { iconType: "square" })
|
|
689
|
+
}
|
|
690
|
+
) : null,
|
|
691
|
+
series.map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
692
|
+
import_recharts2.Bar,
|
|
693
|
+
{
|
|
694
|
+
dataKey: s.key,
|
|
695
|
+
name: s.label,
|
|
696
|
+
fill: colorForSeriesIndex(i, palette),
|
|
697
|
+
stackId: stacked ? "default" : s.key,
|
|
698
|
+
radius: barRadius(stacked, i, series.length),
|
|
699
|
+
children: resolvedShowValueLabels ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_recharts2.LabelList, { dataKey: s.key, position: "top", style: CHART_VALUE_LABEL_STYLE }) : null
|
|
700
|
+
},
|
|
701
|
+
s.key
|
|
702
|
+
))
|
|
703
|
+
] }) });
|
|
704
|
+
}
|
|
705
|
+
function barRadius(stacked, seriesIndex, seriesCount) {
|
|
706
|
+
if (!stacked) return [6, 6, 0, 0];
|
|
707
|
+
const isTopSegment = seriesIndex === seriesCount - 1;
|
|
708
|
+
return isTopSegment ? [6, 6, 0, 0] : [0, 0, 0, 0];
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
// src/components/artifacts/chart-artifact/chart-variant-services/constants.ts
|
|
712
|
+
var VARIANT_CHART_HEIGHT = "100%";
|
|
713
|
+
|
|
714
|
+
// src/components/artifacts/chart-artifact/chart-variant-services/bar-chart-variant-component.tsx
|
|
715
|
+
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
716
|
+
function BarChartVariantComponent({ config, rows }) {
|
|
717
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
718
|
+
BarChart,
|
|
719
|
+
{
|
|
720
|
+
data: rows,
|
|
721
|
+
xKey: config.xKey,
|
|
722
|
+
series: config.series,
|
|
723
|
+
stacked: config.stacked,
|
|
724
|
+
height: VARIANT_CHART_HEIGHT
|
|
725
|
+
}
|
|
726
|
+
);
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
// src/components/artifacts/chart-artifact/chart-variant-services/shared-column-keys.ts
|
|
730
|
+
function seriesBasedColumnKeys(config) {
|
|
731
|
+
return [config.xKey, ...config.series.map((s) => s.key)];
|
|
732
|
+
}
|
|
733
|
+
function pieColumnKeys(config) {
|
|
734
|
+
return [config.categoryKey, config.valueKey];
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
// src/components/artifacts/chart-artifact/chart-variant-services/shared-validators.ts
|
|
738
|
+
function isValidChartSeries(series) {
|
|
739
|
+
if (!series || typeof series !== "object") return false;
|
|
740
|
+
const candidate = series;
|
|
741
|
+
if (typeof candidate.key !== "string" || candidate.key.length === 0) return false;
|
|
742
|
+
if (typeof candidate.label !== "string") return false;
|
|
743
|
+
return true;
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
// src/components/artifacts/chart-artifact/chart-variant-services/bar-chart-variant-service.tsx
|
|
747
|
+
function isValidBarChartConfig(config) {
|
|
748
|
+
if (!config || typeof config !== "object") return false;
|
|
749
|
+
const candidate = config;
|
|
750
|
+
if (candidate.variant !== "BAR") return false;
|
|
751
|
+
if (typeof candidate.xKey !== "string" || candidate.xKey.length === 0) return false;
|
|
752
|
+
if (!Array.isArray(candidate.series) || candidate.series.length === 0) return false;
|
|
753
|
+
if (!candidate.series.every(isValidChartSeries)) return false;
|
|
754
|
+
return true;
|
|
755
|
+
}
|
|
756
|
+
var BarChartVariantService = {
|
|
757
|
+
variant: "BAR",
|
|
758
|
+
isValidConfig: isValidBarChartConfig,
|
|
759
|
+
columnKeys: seriesBasedColumnKeys,
|
|
760
|
+
Component: BarChartVariantComponent
|
|
761
|
+
};
|
|
762
|
+
|
|
763
|
+
// src/components/chart/line-chart/line-chart.tsx
|
|
764
|
+
var import_react6 = require("react");
|
|
765
|
+
var import_recharts3 = require("recharts");
|
|
766
|
+
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
767
|
+
var TARGET_VISIBLE_LABELS = 5;
|
|
768
|
+
function LineChart({
|
|
769
|
+
data,
|
|
770
|
+
xKey,
|
|
771
|
+
series,
|
|
772
|
+
smooth = true,
|
|
773
|
+
showDots = true,
|
|
774
|
+
strokeWidth = 2,
|
|
775
|
+
height = 320,
|
|
776
|
+
width = "100%",
|
|
777
|
+
className,
|
|
778
|
+
palette = CHART_PALETTE,
|
|
779
|
+
showTooltip = true,
|
|
780
|
+
showLegend,
|
|
781
|
+
showGrid = true,
|
|
782
|
+
showXAxis = true,
|
|
783
|
+
showYAxis = true,
|
|
784
|
+
showValueLabels = false,
|
|
785
|
+
xAxisInterval = 0,
|
|
786
|
+
xAxisAngle = 0,
|
|
787
|
+
xAxisTickFormatter
|
|
788
|
+
}) {
|
|
789
|
+
const seriesKeys = (0, import_react6.useMemo)(() => series.map((s) => s.key), [series]);
|
|
790
|
+
const coercedData = (0, import_react6.useMemo)(
|
|
791
|
+
() => coerceNumericColumns(data, seriesKeys),
|
|
792
|
+
[data, seriesKeys]
|
|
793
|
+
);
|
|
794
|
+
const lineType = smooth ? "monotone" : "linear";
|
|
795
|
+
const resolvedShowLegend = showLegend ?? series.length > 1;
|
|
796
|
+
const resolvedTickFormatter = (0, import_react6.useMemo)(() => {
|
|
797
|
+
if (xAxisTickFormatter) return xAxisTickFormatter;
|
|
798
|
+
const visible = evenlyDistributedVisibleIndices(coercedData.length, TARGET_VISIBLE_LABELS);
|
|
799
|
+
return defaultLineChartTickFormatter(coercedData.length, visible);
|
|
800
|
+
}, [xAxisTickFormatter, coercedData.length]);
|
|
801
|
+
const angledLabelPad = xAxisAngle === 0 ? 0 : Math.min(Math.abs(xAxisAngle) * 0.7, 40);
|
|
802
|
+
const chartMargin = { ...CHART_MARGIN, right: CHART_MARGIN.right + 16, bottom: CHART_MARGIN.bottom + angledLabelPad };
|
|
803
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ChartContainer, { height, width, className, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_recharts3.LineChart, { data: coercedData, margin: chartMargin, children: [
|
|
804
|
+
showGrid ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_recharts3.CartesianGrid, { ...CHART_GRID_PROPS }) : null,
|
|
805
|
+
showXAxis ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
806
|
+
import_recharts3.XAxis,
|
|
807
|
+
{
|
|
808
|
+
dataKey: xKey,
|
|
809
|
+
...CHART_AXIS_PROPS,
|
|
810
|
+
interval: xAxisInterval,
|
|
811
|
+
angle: xAxisAngle,
|
|
812
|
+
textAnchor: xAxisAngle !== 0 ? "end" : "middle",
|
|
813
|
+
tickMargin: xAxisAngle === 0 ? 8 : 12,
|
|
814
|
+
height: xAxisAngle !== 0 ? 60 : void 0,
|
|
815
|
+
tickFormatter: resolvedTickFormatter
|
|
816
|
+
}
|
|
817
|
+
) : null,
|
|
818
|
+
showYAxis ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_recharts3.YAxis, { ...CHART_AXIS_PROPS, width: 40 }) : null,
|
|
819
|
+
showTooltip ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_recharts3.Tooltip, { content: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ChartTooltipContent, {}) }) : null,
|
|
820
|
+
resolvedShowLegend ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
821
|
+
import_recharts3.Legend,
|
|
822
|
+
{
|
|
823
|
+
verticalAlign: "top",
|
|
824
|
+
align: "center",
|
|
825
|
+
wrapperStyle: { top: 0 },
|
|
826
|
+
itemSorter: null,
|
|
827
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ChartLegendContent, { iconType: "line" })
|
|
828
|
+
}
|
|
829
|
+
) : null,
|
|
830
|
+
series.map((s, i) => {
|
|
831
|
+
const color = colorForSeriesIndex(i, palette);
|
|
832
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
833
|
+
import_recharts3.Line,
|
|
834
|
+
{
|
|
835
|
+
type: lineType,
|
|
836
|
+
dataKey: s.key,
|
|
837
|
+
name: s.label,
|
|
838
|
+
stroke: color,
|
|
839
|
+
strokeWidth,
|
|
840
|
+
dot: showDots ? { r: 3, fill: color, stroke: color, strokeWidth: 0 } : false,
|
|
841
|
+
activeDot: { r: 5, fill: color, stroke: LINE_ACTIVE_DOT_STROKE, strokeWidth: 2 },
|
|
842
|
+
children: showValueLabels ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_recharts3.LabelList, { dataKey: s.key, position: "top", style: CHART_VALUE_LABEL_STYLE }) : null
|
|
843
|
+
},
|
|
844
|
+
s.key
|
|
845
|
+
);
|
|
846
|
+
})
|
|
847
|
+
] }) });
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
// src/components/artifacts/chart-artifact/chart-variant-services/line-chart-variant-component.tsx
|
|
851
|
+
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
852
|
+
function LineChartVariantComponent({ config, rows }) {
|
|
853
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(LineChart, { data: rows, xKey: config.xKey, series: config.series, height: VARIANT_CHART_HEIGHT });
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
// src/components/artifacts/chart-artifact/chart-variant-services/line-chart-variant-service.tsx
|
|
857
|
+
function isValidLineChartConfig(config) {
|
|
858
|
+
if (!config || typeof config !== "object") return false;
|
|
859
|
+
const candidate = config;
|
|
860
|
+
if (candidate.variant !== "LINE") return false;
|
|
861
|
+
if (typeof candidate.xKey !== "string" || candidate.xKey.length === 0) return false;
|
|
862
|
+
if (!Array.isArray(candidate.series) || candidate.series.length === 0) return false;
|
|
863
|
+
if (!candidate.series.every(isValidChartSeries)) return false;
|
|
864
|
+
return true;
|
|
865
|
+
}
|
|
866
|
+
var LineChartVariantService = {
|
|
867
|
+
variant: "LINE",
|
|
868
|
+
isValidConfig: isValidLineChartConfig,
|
|
869
|
+
columnKeys: seriesBasedColumnKeys,
|
|
870
|
+
Component: LineChartVariantComponent
|
|
871
|
+
};
|
|
872
|
+
|
|
873
|
+
// src/components/chart/pie-chart/pie-chart.tsx
|
|
874
|
+
var import_react7 = require("react");
|
|
875
|
+
var import_recharts4 = require("recharts");
|
|
876
|
+
var import_jsx_runtime13 = require("react/jsx-runtime");
|
|
877
|
+
function PieChart({
|
|
878
|
+
data,
|
|
879
|
+
categoryKey,
|
|
880
|
+
valueKey,
|
|
881
|
+
height = 320,
|
|
882
|
+
width = "100%",
|
|
883
|
+
className,
|
|
884
|
+
palette = CHART_PALETTE,
|
|
885
|
+
showLabels = true,
|
|
886
|
+
showTooltip = true,
|
|
887
|
+
showLegend = true,
|
|
888
|
+
innerRadius = 0,
|
|
889
|
+
outerRadius = "68%",
|
|
890
|
+
othersCategoryLabels = DEFAULT_OTHERS_CATEGORY_LABELS
|
|
891
|
+
}) {
|
|
892
|
+
const { orderedData, sliceColors } = (0, import_react7.useMemo)(() => {
|
|
893
|
+
const coerced = coerceNumericColumns(data, [valueKey]);
|
|
894
|
+
const namedRows = [];
|
|
895
|
+
const namedColors = [];
|
|
896
|
+
const othersRows = [];
|
|
897
|
+
const othersColors = [];
|
|
898
|
+
for (const row of coerced) {
|
|
899
|
+
if (isOthersCategory(row[categoryKey], othersCategoryLabels)) {
|
|
900
|
+
othersRows.push(row);
|
|
901
|
+
othersColors.push(OTHERS_SLICE_COLOR);
|
|
902
|
+
} else {
|
|
903
|
+
namedRows.push(row);
|
|
904
|
+
namedColors.push(colorForSeriesIndex(namedRows.length - 1, palette));
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
return {
|
|
908
|
+
orderedData: [...namedRows, ...othersRows],
|
|
909
|
+
sliceColors: [...namedColors, ...othersColors]
|
|
910
|
+
};
|
|
911
|
+
}, [data, valueKey, categoryKey, othersCategoryLabels, palette]);
|
|
912
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ChartContainer, { height, width, className, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_recharts4.PieChart, { margin: PIE_CHART_MARGIN, children: [
|
|
913
|
+
showTooltip ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_recharts4.Tooltip, { content: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ChartTooltipContent, {}) }) : null,
|
|
914
|
+
showLegend ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
915
|
+
import_recharts4.Legend,
|
|
916
|
+
{
|
|
917
|
+
verticalAlign: "top",
|
|
918
|
+
align: "center",
|
|
919
|
+
wrapperStyle: { top: 0 },
|
|
920
|
+
itemSorter: null,
|
|
921
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ChartLegendContent, { iconType: "square" })
|
|
922
|
+
}
|
|
923
|
+
) : null,
|
|
924
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
925
|
+
import_recharts4.Pie,
|
|
926
|
+
{
|
|
927
|
+
data: orderedData,
|
|
928
|
+
dataKey: valueKey,
|
|
929
|
+
nameKey: categoryKey,
|
|
930
|
+
cx: "50%",
|
|
931
|
+
cy: "52%",
|
|
932
|
+
startAngle: 90,
|
|
933
|
+
endAngle: -270,
|
|
934
|
+
innerRadius,
|
|
935
|
+
outerRadius,
|
|
936
|
+
label: showLabels ? (entry) => {
|
|
937
|
+
const v = entry.value;
|
|
938
|
+
if (v === void 0 || v === null) return null;
|
|
939
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
940
|
+
"text",
|
|
941
|
+
{
|
|
942
|
+
x: entry.x,
|
|
943
|
+
y: entry.y,
|
|
944
|
+
fill: CHART_VALUE_LABEL_STYLE.fill,
|
|
945
|
+
fontSize: CHART_VALUE_LABEL_STYLE.fontSize,
|
|
946
|
+
fontWeight: CHART_VALUE_LABEL_STYLE.fontWeight,
|
|
947
|
+
textAnchor: entry.textAnchor,
|
|
948
|
+
dominantBaseline: "central",
|
|
949
|
+
children: String(v)
|
|
950
|
+
}
|
|
951
|
+
);
|
|
952
|
+
} : false,
|
|
953
|
+
labelLine: false,
|
|
954
|
+
stroke: "none",
|
|
955
|
+
strokeWidth: 0,
|
|
956
|
+
children: orderedData.map((_row, i) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_recharts4.Cell, { fill: sliceColors[i] }, i))
|
|
957
|
+
}
|
|
958
|
+
)
|
|
959
|
+
] }) });
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
// src/components/artifacts/chart-artifact/chart-variant-services/pie-chart-variant-component.tsx
|
|
963
|
+
var import_jsx_runtime14 = require("react/jsx-runtime");
|
|
964
|
+
function PieChartVariantComponent({
|
|
965
|
+
config,
|
|
966
|
+
rows,
|
|
967
|
+
labels
|
|
968
|
+
}) {
|
|
969
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
970
|
+
PieChart,
|
|
971
|
+
{
|
|
972
|
+
data: rows,
|
|
973
|
+
categoryKey: config.categoryKey,
|
|
974
|
+
valueKey: config.valueKey,
|
|
975
|
+
othersCategoryLabels: labels.othersCategoryLabels,
|
|
976
|
+
height: VARIANT_CHART_HEIGHT
|
|
977
|
+
}
|
|
978
|
+
);
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
// src/components/artifacts/chart-artifact/chart-variant-services/pie-chart-variant-service.tsx
|
|
982
|
+
function isValidPieChartConfig(config) {
|
|
983
|
+
if (!config || typeof config !== "object") return false;
|
|
984
|
+
const candidate = config;
|
|
985
|
+
if (candidate.variant !== "PIE") return false;
|
|
986
|
+
if (typeof candidate.categoryKey !== "string" || candidate.categoryKey.length === 0) return false;
|
|
987
|
+
if (typeof candidate.valueKey !== "string" || candidate.valueKey.length === 0) return false;
|
|
988
|
+
if (!Array.isArray(candidate.series)) return false;
|
|
989
|
+
return true;
|
|
990
|
+
}
|
|
991
|
+
var PieChartVariantService = {
|
|
992
|
+
variant: "PIE",
|
|
993
|
+
isValidConfig: isValidPieChartConfig,
|
|
994
|
+
columnKeys: pieColumnKeys,
|
|
995
|
+
Component: PieChartVariantComponent
|
|
996
|
+
};
|
|
997
|
+
|
|
998
|
+
// src/components/artifacts/chart-artifact/chart-variant-services/index.ts
|
|
999
|
+
var ChartVariantServices = {
|
|
1000
|
+
LINE: LineChartVariantService,
|
|
1001
|
+
BAR: BarChartVariantService,
|
|
1002
|
+
PIE: PieChartVariantService
|
|
1003
|
+
};
|
|
1004
|
+
|
|
1005
|
+
// src/components/artifacts/chart-artifact/chart-artifact-content.tsx
|
|
1006
|
+
var import_jsx_runtime15 = require("react/jsx-runtime");
|
|
1007
|
+
function ChartArtifactContent({ config, rows, labels }) {
|
|
1008
|
+
const service = ChartVariantServices[config.variant];
|
|
1009
|
+
if (!service) {
|
|
1010
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(StatusMessage, { $tone: "info", children: labels.unavailable });
|
|
1011
|
+
}
|
|
1012
|
+
const Component = service.Component;
|
|
1013
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Component, { config, rows, labels });
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
// src/components/artifacts/parse-csv.ts
|
|
1017
|
+
var import_papaparse = __toESM(require("papaparse"), 1);
|
|
1018
|
+
function parseCsv(text, columns) {
|
|
1019
|
+
const result = import_papaparse.default.parse(text, {
|
|
1020
|
+
header: true,
|
|
1021
|
+
skipEmptyLines: true,
|
|
1022
|
+
dynamicTyping: false
|
|
1023
|
+
});
|
|
1024
|
+
const csvHeaders = new Set(result.meta?.fields ?? []);
|
|
1025
|
+
const presentColumns = columns.filter((col) => csvHeaders.has(col.key));
|
|
1026
|
+
if (presentColumns.length === 0) return [];
|
|
1027
|
+
return result.data.map((rawRow) => {
|
|
1028
|
+
const row = {};
|
|
1029
|
+
for (const column of presentColumns) {
|
|
1030
|
+
const value = rawRow[column.key];
|
|
1031
|
+
row[column.key] = value === void 0 || value === "" ? null : value;
|
|
1032
|
+
}
|
|
1033
|
+
return row;
|
|
1034
|
+
});
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
// src/components/artifacts/use-rows-artifact-data.ts
|
|
1038
|
+
var import_react8 = require("react");
|
|
1039
|
+
var LOADING = { status: "loading", rows: null, error: null };
|
|
1040
|
+
var UNAVAILABLE = { status: "unavailable", rows: null, error: null };
|
|
1041
|
+
var INITIAL_INTERNAL = { cacheKey: null, data: LOADING };
|
|
1042
|
+
function columnsFingerprint(columns) {
|
|
1043
|
+
return columns.map((c) => c.key).join("|");
|
|
1044
|
+
}
|
|
1045
|
+
function scrubUrl(url) {
|
|
1046
|
+
const q = url.indexOf("?");
|
|
1047
|
+
return q === -1 ? url : `${url.slice(0, q)}?<redacted>`;
|
|
1048
|
+
}
|
|
1049
|
+
function useRowsArtifactData(presignedUrl, columns, parse) {
|
|
1050
|
+
const colsKey = (0, import_react8.useMemo)(() => columnsFingerprint(columns), [columns]);
|
|
1051
|
+
const safeUrl = presignedUrl && isSafeDownloadUrl(presignedUrl) ? presignedUrl : null;
|
|
1052
|
+
const cacheKey = safeUrl ? `${safeUrl}::${colsKey}` : null;
|
|
1053
|
+
const [state, setState] = (0, import_react8.useState)(INITIAL_INTERNAL);
|
|
1054
|
+
(0, import_react8.useEffect)(() => {
|
|
1055
|
+
if (!safeUrl) return;
|
|
1056
|
+
const controller = new AbortController();
|
|
1057
|
+
fetch(safeUrl, {
|
|
1058
|
+
signal: controller.signal,
|
|
1059
|
+
headers: { Accept: "text/csv" },
|
|
1060
|
+
// Presigned URLs are cross-origin S3 in production and don't need credentials. Explicit
|
|
1061
|
+
// omit so a same-origin URL (compromised payload, local dev) can't accidentally receive
|
|
1062
|
+
// session cookies or auth headers.
|
|
1063
|
+
credentials: "omit"
|
|
1064
|
+
}).then(async (response) => {
|
|
1065
|
+
if (!response.ok) throw new Error(`Failed to load artifact data (${response.status})`);
|
|
1066
|
+
return response.text();
|
|
1067
|
+
}).then((text) => {
|
|
1068
|
+
if (controller.signal.aborted) return;
|
|
1069
|
+
try {
|
|
1070
|
+
const rows = parse(text, columns);
|
|
1071
|
+
setState({ cacheKey: `${safeUrl}::${colsKey}`, data: { status: "ready", rows, error: null } });
|
|
1072
|
+
} catch (parseError) {
|
|
1073
|
+
const err = parseError instanceof Error ? parseError : new Error(String(parseError));
|
|
1074
|
+
console.error("[ArtifactData] CSV parse failed:", err.message);
|
|
1075
|
+
setState({ cacheKey: `${safeUrl}::${colsKey}`, data: { status: "error", rows: null, error: err } });
|
|
1076
|
+
}
|
|
1077
|
+
}).catch((error) => {
|
|
1078
|
+
if (controller.signal.aborted) return;
|
|
1079
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
1080
|
+
console.error(`[ArtifactData] Fetch failed [${scrubUrl(safeUrl)}]:`, err.message);
|
|
1081
|
+
setState({ cacheKey: `${safeUrl}::${colsKey}`, data: { status: "error", rows: null, error: err } });
|
|
1082
|
+
});
|
|
1083
|
+
return () => {
|
|
1084
|
+
controller.abort();
|
|
1085
|
+
};
|
|
1086
|
+
}, [safeUrl, colsKey]);
|
|
1087
|
+
if (!safeUrl) return UNAVAILABLE;
|
|
1088
|
+
if (state.cacheKey !== cacheKey) return LOADING;
|
|
1089
|
+
return state.data;
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
// src/components/artifacts/chart-artifact/use-chart-artifact-data.ts
|
|
1093
|
+
function useChartArtifactData(presignedUrl, config) {
|
|
1094
|
+
const service = ChartVariantServices[config.variant];
|
|
1095
|
+
const keys = service ? service.columnKeys(config) : [];
|
|
1096
|
+
const columns = keys.map((key) => ({ key }));
|
|
1097
|
+
return useRowsArtifactData(presignedUrl, columns, parseCsv);
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
// src/components/artifacts/chart-artifact/validate.ts
|
|
1101
|
+
function isValidChartConfig(config) {
|
|
1102
|
+
if (!config || typeof config !== "object") return false;
|
|
1103
|
+
const variant = config.variant;
|
|
1104
|
+
if (typeof variant !== "string") return false;
|
|
1105
|
+
const service = ChartVariantServices[variant];
|
|
1106
|
+
if (!service) return false;
|
|
1107
|
+
return service.isValidConfig(config);
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
// src/components/artifacts/chart-artifact/chart-artifact.tsx
|
|
1111
|
+
var import_jsx_runtime16 = require("react/jsx-runtime");
|
|
1112
|
+
var TITLE_CLASSES = "text-sm font-semibold text-foreground";
|
|
1113
|
+
var CHART_HEIGHT = 320;
|
|
1114
|
+
function ChartArtifact({ artifact, labels, callbacks }) {
|
|
1115
|
+
const validConfig = isValidChartConfig(artifact.config);
|
|
1116
|
+
(0, import_react9.useEffect)(() => {
|
|
1117
|
+
if (validConfig) return;
|
|
1118
|
+
console.warn("[clay] ChartArtifact: malformed `config` \u2014 skipping render.");
|
|
1119
|
+
}, [validConfig]);
|
|
1120
|
+
const presignedUrl = artifact.protectedAsset?.presignedUrl ?? null;
|
|
1121
|
+
const resolvedLabels = (0, import_react9.useMemo)(
|
|
1122
|
+
() => ({ ...DEFAULT_CHART_ARTIFACT_LABELS, ...labels?.[artifactTypes.CHART] ?? {} }),
|
|
1123
|
+
[labels]
|
|
1124
|
+
);
|
|
1125
|
+
const data = useChartArtifactData(
|
|
1126
|
+
validConfig ? presignedUrl : null,
|
|
1127
|
+
validConfig ? artifact.config : { variant: "LINE", series: [], xKey: "x" }
|
|
1128
|
+
);
|
|
1129
|
+
const onDownload = callbacks?.[artifactTypes.CHART]?.onDownload;
|
|
1130
|
+
const handleDownloadClick = onDownload ? () => onDownload(artifact) : void 0;
|
|
1131
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(Artifact, { className: "shadow-none w-full rounded-2xl bg-background/70", children: [
|
|
1132
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(ArtifactHeader, { className: "bg-background/70", children: [
|
|
1133
|
+
artifact.title ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(ArtifactTitle, { className: TITLE_CLASSES, children: artifact.title }) : /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", {}),
|
|
1134
|
+
presignedUrl ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(ArtifactActions, { children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(ArtifactDownloadButton, { href: presignedUrl, label: resolvedLabels.download, onClick: handleDownloadClick }) }) : null
|
|
1135
|
+
] }),
|
|
1136
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
1137
|
+
ArtifactContent,
|
|
1138
|
+
{
|
|
1139
|
+
className: "p-4 overflow-visible flex flex-none items-center justify-center",
|
|
1140
|
+
style: { height: CHART_HEIGHT },
|
|
1141
|
+
children: renderBody(validConfig, artifact, data, resolvedLabels)
|
|
1142
|
+
}
|
|
1143
|
+
)
|
|
1144
|
+
] });
|
|
1145
|
+
}
|
|
1146
|
+
function renderBody(validConfig, artifact, data, labels) {
|
|
1147
|
+
if (!validConfig) {
|
|
1148
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(StatusMessage, { $tone: "info", children: labels.unavailable });
|
|
1149
|
+
}
|
|
1150
|
+
if (data.status === "error") {
|
|
1151
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(StatusMessage, { $tone: "error", children: labels.loadError });
|
|
1152
|
+
}
|
|
1153
|
+
if (data.status === "unavailable") {
|
|
1154
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(StatusMessage, { $tone: "info", children: labels.unavailable });
|
|
1155
|
+
}
|
|
1156
|
+
if (data.status === "loading") {
|
|
1157
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(StatusMessage, { $tone: "info", children: labels.loading });
|
|
1158
|
+
}
|
|
1159
|
+
if (data.rows.length === 0) {
|
|
1160
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(StatusMessage, { $tone: "info", children: labels.empty });
|
|
1161
|
+
}
|
|
1162
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(ChartArtifactContent, { config: artifact.config, rows: data.rows, labels });
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
// src/components/artifacts/chart-artifact/to-clipboard-text.ts
|
|
1166
|
+
function chartArtifactToClipboardText(artifact) {
|
|
1167
|
+
const trimmedTitle = artifact.title?.trim();
|
|
1168
|
+
return trimmedTitle || "Chart";
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
// src/components/artifacts/artifact-services/chart-artifact-service.ts
|
|
1172
|
+
var ChartArtifactService = {
|
|
1173
|
+
type: artifactTypes.CHART,
|
|
1174
|
+
Component: ChartArtifact,
|
|
1175
|
+
toClipboardText: chartArtifactToClipboardText
|
|
1176
|
+
};
|
|
1177
|
+
|
|
1178
|
+
// src/components/artifacts/table-artifact/table-artifact.tsx
|
|
1179
|
+
var import_react11 = require("react");
|
|
1180
|
+
|
|
399
1181
|
// src/components/ui/table.tsx
|
|
400
1182
|
var React2 = __toESM(require("react"), 1);
|
|
401
|
-
var
|
|
402
|
-
var Table = React2.forwardRef(({ className, containerClassName, ...props }, ref) => /* @__PURE__ */ (0,
|
|
1183
|
+
var import_jsx_runtime17 = require("react/jsx-runtime");
|
|
1184
|
+
var Table = React2.forwardRef(({ className, containerClassName, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: cn("relative w-full overflow-auto", containerClassName), children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("table", { ref, className: cn("w-full caption-bottom text-sm", className), ...props }) }));
|
|
403
1185
|
Table.displayName = "Table";
|
|
404
1186
|
var TableHeader = React2.forwardRef(
|
|
405
|
-
({ className, ...props }, ref) => /* @__PURE__ */ (0,
|
|
1187
|
+
({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("thead", { ref, className: cn("[&_tr]:border-b", className), ...props })
|
|
406
1188
|
);
|
|
407
1189
|
TableHeader.displayName = "TableHeader";
|
|
408
1190
|
var TableBody = React2.forwardRef(
|
|
409
|
-
({ className, ...props }, ref) => /* @__PURE__ */ (0,
|
|
1191
|
+
({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("tbody", { ref, className: cn("[&_tr:last-child]:border-0", className), ...props })
|
|
410
1192
|
);
|
|
411
1193
|
TableBody.displayName = "TableBody";
|
|
412
1194
|
var TableFooter = React2.forwardRef(
|
|
413
|
-
({ className, ...props }, ref) => /* @__PURE__ */ (0,
|
|
1195
|
+
({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("tfoot", { ref, className: cn("border-t bg-muted/50 font-medium [&>tr]:last:border-b-0", className), ...props })
|
|
414
1196
|
);
|
|
415
1197
|
TableFooter.displayName = "TableFooter";
|
|
416
1198
|
var TableRow = React2.forwardRef(
|
|
417
|
-
({ className, ...props }, ref) => /* @__PURE__ */ (0,
|
|
1199
|
+
({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
418
1200
|
"tr",
|
|
419
1201
|
{
|
|
420
1202
|
ref,
|
|
@@ -425,7 +1207,7 @@ var TableRow = React2.forwardRef(
|
|
|
425
1207
|
);
|
|
426
1208
|
TableRow.displayName = "TableRow";
|
|
427
1209
|
var TableHead = React2.forwardRef(
|
|
428
|
-
({ className, ...props }, ref) => /* @__PURE__ */ (0,
|
|
1210
|
+
({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
429
1211
|
"th",
|
|
430
1212
|
{
|
|
431
1213
|
ref,
|
|
@@ -439,20 +1221,20 @@ var TableHead = React2.forwardRef(
|
|
|
439
1221
|
);
|
|
440
1222
|
TableHead.displayName = "TableHead";
|
|
441
1223
|
var TableCell = React2.forwardRef(
|
|
442
|
-
({ className, ...props }, ref) => /* @__PURE__ */ (0,
|
|
1224
|
+
({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("td", { ref, className: cn("p-4 align-middle [&:has([role=checkbox])]:pr-0", className), ...props })
|
|
443
1225
|
);
|
|
444
1226
|
TableCell.displayName = "TableCell";
|
|
445
1227
|
var TableCaption = React2.forwardRef(
|
|
446
|
-
({ className, ...props }, ref) => /* @__PURE__ */ (0,
|
|
1228
|
+
({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("caption", { ref, className: cn("mt-4 text-sm text-muted-foreground", className), ...props })
|
|
447
1229
|
);
|
|
448
1230
|
TableCaption.displayName = "TableCaption";
|
|
449
1231
|
|
|
450
1232
|
// src/components/table/use-scroll-shadow.ts
|
|
451
|
-
var
|
|
1233
|
+
var import_react10 = require("react");
|
|
452
1234
|
function useScrollShadow() {
|
|
453
|
-
const targetRef = (0,
|
|
454
|
-
const [isScrolled, setIsScrolled] = (0,
|
|
455
|
-
(0,
|
|
1235
|
+
const targetRef = (0, import_react10.useRef)(null);
|
|
1236
|
+
const [isScrolled, setIsScrolled] = (0, import_react10.useState)(false);
|
|
1237
|
+
(0, import_react10.useEffect)(() => {
|
|
456
1238
|
const target = targetRef.current;
|
|
457
1239
|
if (!target) return;
|
|
458
1240
|
let scrollContainer = target;
|
|
@@ -473,18 +1255,6 @@ function useScrollShadow() {
|
|
|
473
1255
|
return { targetRef, isScrolled };
|
|
474
1256
|
}
|
|
475
1257
|
|
|
476
|
-
// src/components/artifacts/table-artifact/table-artifact-styles.ts
|
|
477
|
-
var import_react5 = require("react");
|
|
478
|
-
function StatusMessage({ $tone, children }) {
|
|
479
|
-
return (0, import_react5.createElement)(
|
|
480
|
-
"div",
|
|
481
|
-
{
|
|
482
|
-
className: `py-6 px-2 text-center text-sm font-medium leading-5 ${$tone === "error" ? "text-red-600" : "text-brown-70"}`
|
|
483
|
-
},
|
|
484
|
-
children
|
|
485
|
-
);
|
|
486
|
-
}
|
|
487
|
-
|
|
488
1258
|
// src/components/artifacts/table-artifact/table-artifact-content.constants.ts
|
|
489
1259
|
var TABLE_STATUS = {
|
|
490
1260
|
LOADING: "loading",
|
|
@@ -504,125 +1274,58 @@ var DATA_ROW_CLASSES = `${ROW_CLASSES} animate-in fade-in duration-300`;
|
|
|
504
1274
|
var SCROLL_SHADOW = "0 1px 2px rgb(0 0 0 / 0.01), 0 6px 12px -2px rgb(0 0 0 / 0.03)";
|
|
505
1275
|
|
|
506
1276
|
// src/components/artifacts/table-artifact/table-artifact-content.tsx
|
|
507
|
-
var
|
|
1277
|
+
var import_jsx_runtime18 = require("react/jsx-runtime");
|
|
508
1278
|
function TableArtifactContent({
|
|
509
1279
|
columns,
|
|
510
1280
|
data,
|
|
511
1281
|
knownRowCount,
|
|
512
1282
|
maxShimmerRows,
|
|
513
1283
|
maxBodyHeight,
|
|
514
|
-
|
|
1284
|
+
labels
|
|
515
1285
|
}) {
|
|
516
1286
|
if (data.status === TABLE_STATUS.ERROR) {
|
|
517
|
-
return /* @__PURE__ */ (0,
|
|
1287
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(StatusMessage, { $tone: "error", children: labels.loadError });
|
|
518
1288
|
}
|
|
519
1289
|
if (data.status === TABLE_STATUS.UNAVAILABLE) {
|
|
520
|
-
return /* @__PURE__ */ (0,
|
|
1290
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(StatusMessage, { $tone: "info", children: labels.unavailable });
|
|
521
1291
|
}
|
|
522
1292
|
if (data.status === TABLE_STATUS.LOADING) {
|
|
523
1293
|
if (typeof knownRowCount === "number" && knownRowCount > 0) {
|
|
524
1294
|
const shimmerCount = Math.min(knownRowCount, maxShimmerRows);
|
|
525
|
-
return /* @__PURE__ */ (0,
|
|
1295
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ArtifactTable, { columns, maxBodyHeight, children: renderShimmerRows(columns, shimmerCount) });
|
|
526
1296
|
}
|
|
527
|
-
return /* @__PURE__ */ (0,
|
|
1297
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(StatusMessage, { $tone: "info", children: labels.loading });
|
|
528
1298
|
}
|
|
529
|
-
return /* @__PURE__ */ (0,
|
|
1299
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ArtifactTable, { columns, maxBodyHeight, children: data.rows.length === 0 ? renderEmptyRow(columns.length, labels.empty) : renderDataRows(columns, data.rows) });
|
|
530
1300
|
}
|
|
531
1301
|
function ArtifactTable({ columns, maxBodyHeight, children }) {
|
|
532
1302
|
const { targetRef, isScrolled } = useScrollShadow();
|
|
533
|
-
return /* @__PURE__ */ (0,
|
|
534
|
-
/* @__PURE__ */ (0,
|
|
1303
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { ref: targetRef, className: "overscroll-none", style: { maxHeight: maxBodyHeight, overflowY: "auto" }, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(Table, { containerClassName: "overflow-visible", children: [
|
|
1304
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
535
1305
|
TableHeader,
|
|
536
1306
|
{
|
|
537
1307
|
"data-scrolled": isScrolled ? "true" : void 0,
|
|
538
1308
|
className: "sticky top-0 z-10 bg-background/70 backdrop-blur-lg transition-shadow",
|
|
539
1309
|
style: isScrolled ? { boxShadow: SCROLL_SHADOW } : void 0,
|
|
540
|
-
children: /* @__PURE__ */ (0,
|
|
1310
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(TableRow, { className: "hover:bg-transparent", children: columns.map((column) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(TableHead, { scope: "col", className: HEAD_CLASS, children: column.label }, column.key)) })
|
|
541
1311
|
}
|
|
542
1312
|
),
|
|
543
|
-
/* @__PURE__ */ (0,
|
|
1313
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(TableBody, { children })
|
|
544
1314
|
] }) });
|
|
545
1315
|
}
|
|
546
1316
|
function renderShimmerRows(columns, count) {
|
|
547
|
-
return Array.from({ length: count }).map((_, index) => /* @__PURE__ */ (0,
|
|
1317
|
+
return Array.from({ length: count }).map((_, index) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(TableRow, { className: ROW_CLASSES, "aria-hidden": "true", children: columns.map((column) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(TableCell, { className: CELL_CLASS, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "block h-3 w-3/4 animate-pulse rounded bg-muted-foreground/20" }) }, column.key)) }, `shimmer-${index}`));
|
|
548
1318
|
}
|
|
549
1319
|
function renderDataRows(columns, rows) {
|
|
550
|
-
return rows.map((row, index) => /* @__PURE__ */ (0,
|
|
1320
|
+
return rows.map((row, index) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(TableRow, { className: DATA_ROW_CLASSES, children: columns.map((column) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(TableCell, { className: CELL_CLASS, children: formatCell(row, column) }, column.key)) }, index));
|
|
551
1321
|
}
|
|
552
|
-
function renderEmptyRow(colSpan) {
|
|
553
|
-
return /* @__PURE__ */ (0,
|
|
1322
|
+
function renderEmptyRow(colSpan, emptyMessage) {
|
|
1323
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(TableRow, { className: "hover:bg-transparent", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(TableCell, { colSpan, className: "py-6 text-center text-sm text-muted-foreground", children: emptyMessage }) });
|
|
554
1324
|
}
|
|
555
1325
|
|
|
556
1326
|
// src/components/artifacts/table-artifact/use-table-artifact-data.ts
|
|
557
|
-
var import_react6 = require("react");
|
|
558
|
-
|
|
559
|
-
// src/components/artifacts/table-artifact/parse-csv.ts
|
|
560
|
-
var import_papaparse = __toESM(require("papaparse"), 1);
|
|
561
|
-
function parseCsv(text, columns) {
|
|
562
|
-
const result = import_papaparse.default.parse(text, {
|
|
563
|
-
header: true,
|
|
564
|
-
skipEmptyLines: true,
|
|
565
|
-
dynamicTyping: false
|
|
566
|
-
});
|
|
567
|
-
const csvHeaders = new Set(result.meta?.fields ?? []);
|
|
568
|
-
const presentColumns = columns.filter((col) => csvHeaders.has(col.key));
|
|
569
|
-
return result.data.map((rawRow) => {
|
|
570
|
-
const row = {};
|
|
571
|
-
for (const column of presentColumns) {
|
|
572
|
-
const value = rawRow[column.key];
|
|
573
|
-
row[column.key] = value === void 0 || value === "" ? null : value;
|
|
574
|
-
}
|
|
575
|
-
return row;
|
|
576
|
-
});
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
// src/components/artifacts/table-artifact/use-table-artifact-data.ts
|
|
580
|
-
var LOADING = { status: "loading", rows: null, error: null };
|
|
581
|
-
var UNAVAILABLE = { status: "unavailable", rows: null, error: null };
|
|
582
|
-
var INITIAL_INTERNAL = { cacheKey: null, data: LOADING };
|
|
583
|
-
function columnsFingerprint(columns) {
|
|
584
|
-
return columns.map((c) => `${c.key}:${c.type ?? "string"}`).join("|");
|
|
585
|
-
}
|
|
586
1327
|
function useTableArtifactData(presignedUrl, columns) {
|
|
587
|
-
|
|
588
|
-
const cacheKey = presignedUrl ? `${presignedUrl}::${colsKey}` : null;
|
|
589
|
-
const [state, setState] = (0, import_react6.useState)(INITIAL_INTERNAL);
|
|
590
|
-
(0, import_react6.useEffect)(() => {
|
|
591
|
-
if (!presignedUrl) return;
|
|
592
|
-
const controller = new AbortController();
|
|
593
|
-
fetch(presignedUrl, {
|
|
594
|
-
signal: controller.signal,
|
|
595
|
-
headers: { Accept: "text/csv" },
|
|
596
|
-
// Presigned URLs are cross-origin S3 in production and don't need credentials. Explicit
|
|
597
|
-
// omit so a same-origin URL (compromised payload, local dev) can't accidentally receive
|
|
598
|
-
// session cookies or auth headers.
|
|
599
|
-
credentials: "omit"
|
|
600
|
-
}).then(async (response) => {
|
|
601
|
-
if (!response.ok) throw new Error(`Failed to load table data (${response.status})`);
|
|
602
|
-
return response.text();
|
|
603
|
-
}).then((text) => {
|
|
604
|
-
if (controller.signal.aborted) return;
|
|
605
|
-
try {
|
|
606
|
-
const rows = parseCsv(text, columns);
|
|
607
|
-
setState({ cacheKey: `${presignedUrl}::${colsKey}`, data: { status: "ready", rows, error: null } });
|
|
608
|
-
} catch (parseError) {
|
|
609
|
-
console.error("[TableArtifact] CSV parse failed", parseError);
|
|
610
|
-
const err = parseError instanceof Error ? parseError : new Error(String(parseError));
|
|
611
|
-
setState({ cacheKey: `${presignedUrl}::${colsKey}`, data: { status: "error", rows: null, error: err } });
|
|
612
|
-
}
|
|
613
|
-
}).catch((error) => {
|
|
614
|
-
if (controller.signal.aborted) return;
|
|
615
|
-
console.error("[TableArtifact] Fetch failed", error);
|
|
616
|
-
const err = error instanceof Error ? error : new Error(String(error));
|
|
617
|
-
setState({ cacheKey: `${presignedUrl}::${colsKey}`, data: { status: "error", rows: null, error: err } });
|
|
618
|
-
});
|
|
619
|
-
return () => {
|
|
620
|
-
controller.abort();
|
|
621
|
-
};
|
|
622
|
-
}, [presignedUrl, colsKey]);
|
|
623
|
-
if (!presignedUrl) return UNAVAILABLE;
|
|
624
|
-
if (state.cacheKey !== cacheKey) return LOADING;
|
|
625
|
-
return state.data;
|
|
1328
|
+
return useRowsArtifactData(presignedUrl, columns, parseCsv);
|
|
626
1329
|
}
|
|
627
1330
|
|
|
628
1331
|
// src/components/artifacts/table-artifact/validate.ts
|
|
@@ -642,42 +1345,61 @@ function isValidTableColumn(column) {
|
|
|
642
1345
|
}
|
|
643
1346
|
|
|
644
1347
|
// src/components/artifacts/table-artifact/table-artifact.tsx
|
|
645
|
-
var
|
|
646
|
-
var
|
|
1348
|
+
var import_jsx_runtime19 = require("react/jsx-runtime");
|
|
1349
|
+
var TITLE_CLASSES2 = "text-sm font-semibold text-foreground";
|
|
647
1350
|
var TABLE_MAX_BODY_HEIGHT = 480;
|
|
648
1351
|
var MAX_SHIMMER_ROWS = 10;
|
|
649
|
-
|
|
650
|
-
function TableArtifact({ artifact }) {
|
|
1352
|
+
function TableArtifact({ artifact, labels, callbacks }) {
|
|
651
1353
|
const validConfig = isValidTableConfig(artifact.config);
|
|
652
1354
|
const validColumns = validConfig ? artifact.config.columns : null;
|
|
653
|
-
(0,
|
|
1355
|
+
(0, import_react11.useEffect)(() => {
|
|
654
1356
|
if (validConfig) return;
|
|
655
1357
|
console.warn("[clay] TableArtifact: malformed `config` \u2014 skipping render.");
|
|
656
1358
|
}, [validConfig]);
|
|
1359
|
+
const resolvedLabels = (0, import_react11.useMemo)(
|
|
1360
|
+
() => ({ ...DEFAULT_TABLE_ARTIFACT_LABELS, ...labels?.[artifactTypes.TABLE] ?? {} }),
|
|
1361
|
+
[labels]
|
|
1362
|
+
);
|
|
657
1363
|
const data = useTableArtifactData(
|
|
658
1364
|
validConfig ? artifact.protectedAsset?.presignedUrl ?? null : null,
|
|
659
1365
|
validColumns ?? []
|
|
660
1366
|
);
|
|
661
1367
|
const presignedUrl = artifact.protectedAsset?.presignedUrl ?? null;
|
|
1368
|
+
const onDownload = callbacks?.[artifactTypes.TABLE]?.onDownload;
|
|
1369
|
+
const handleDownloadClick = onDownload ? () => onDownload(artifact) : void 0;
|
|
662
1370
|
if (!validConfig || !validColumns) {
|
|
663
|
-
return /* @__PURE__ */ (0,
|
|
664
|
-
/* @__PURE__ */ (0,
|
|
665
|
-
artifact.title ? /* @__PURE__ */ (0,
|
|
666
|
-
presignedUrl ? /* @__PURE__ */ (0,
|
|
1371
|
+
return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(Artifact, { className: "shadow-none w-full rounded-2xl bg-background/70", children: [
|
|
1372
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(ArtifactHeader, { className: "bg-background/70", children: [
|
|
1373
|
+
artifact.title ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ArtifactTitle, { className: TITLE_CLASSES2, children: artifact.title }) : /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", {}),
|
|
1374
|
+
presignedUrl ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ArtifactActions, { children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
1375
|
+
ArtifactDownloadButton,
|
|
1376
|
+
{
|
|
1377
|
+
href: presignedUrl,
|
|
1378
|
+
label: resolvedLabels.download,
|
|
1379
|
+
onClick: handleDownloadClick
|
|
1380
|
+
}
|
|
1381
|
+
) }) : null
|
|
667
1382
|
] }),
|
|
668
|
-
/* @__PURE__ */ (0,
|
|
1383
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ArtifactContent, { className: "p-0 overflow-auto", style: { maxHeight: TABLE_MAX_BODY_HEIGHT }, children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(StatusMessage, { $tone: "info", children: resolvedLabels.unavailable }) })
|
|
669
1384
|
] });
|
|
670
1385
|
}
|
|
671
1386
|
return (
|
|
672
1387
|
// The table body owns its own scroll container (`ArtifactTable` inside `TableArtifactContent`).
|
|
673
1388
|
// Disable the outer overflow container so only one scroll context exists — this prevents
|
|
674
1389
|
// double-scroll nesting and lets `useScrollShadow` find the correct scroll ancestor.
|
|
675
|
-
/* @__PURE__ */ (0,
|
|
676
|
-
/* @__PURE__ */ (0,
|
|
677
|
-
artifact.title ? /* @__PURE__ */ (0,
|
|
678
|
-
/* @__PURE__ */ (0,
|
|
1390
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(Artifact, { className: "shadow-none w-full rounded-2xl bg-background/70", children: [
|
|
1391
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(ArtifactHeader, { className: "bg-background/70", children: [
|
|
1392
|
+
artifact.title ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ArtifactTitle, { className: TITLE_CLASSES2, children: artifact.title }) : /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", {}),
|
|
1393
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ArtifactActions, { children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
1394
|
+
ArtifactDownloadButton,
|
|
1395
|
+
{
|
|
1396
|
+
href: presignedUrl ?? "",
|
|
1397
|
+
label: resolvedLabels.download,
|
|
1398
|
+
onClick: handleDownloadClick
|
|
1399
|
+
}
|
|
1400
|
+
) })
|
|
679
1401
|
] }),
|
|
680
|
-
/* @__PURE__ */ (0,
|
|
1402
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ArtifactContent, { className: "p-0 overflow-visible", style: { maxHeight: "none" }, children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
681
1403
|
TableArtifactContent,
|
|
682
1404
|
{
|
|
683
1405
|
columns: validColumns,
|
|
@@ -685,7 +1407,7 @@ function TableArtifact({ artifact }) {
|
|
|
685
1407
|
knownRowCount: artifact.config.stats?.rowCount,
|
|
686
1408
|
maxShimmerRows: MAX_SHIMMER_ROWS,
|
|
687
1409
|
maxBodyHeight: TABLE_MAX_BODY_HEIGHT,
|
|
688
|
-
|
|
1410
|
+
labels: resolvedLabels
|
|
689
1411
|
}
|
|
690
1412
|
) })
|
|
691
1413
|
] })
|
|
@@ -707,7 +1429,8 @@ var TableArtifactService = {
|
|
|
707
1429
|
|
|
708
1430
|
// src/components/artifacts/artifact-services/index.ts
|
|
709
1431
|
var ArtifactServices = {
|
|
710
|
-
[artifactTypes.TABLE]: TableArtifactService
|
|
1432
|
+
[artifactTypes.TABLE]: TableArtifactService,
|
|
1433
|
+
[artifactTypes.CHART]: ChartArtifactService
|
|
711
1434
|
};
|
|
712
1435
|
function artifactToClipboardText(artifact) {
|
|
713
1436
|
const entry = ArtifactServices[artifact.type];
|
|
@@ -720,6 +1443,9 @@ function artifactToClipboardText(artifact) {
|
|
|
720
1443
|
0 && (module.exports = {
|
|
721
1444
|
ArtifactDownloadButton,
|
|
722
1445
|
ArtifactServices,
|
|
1446
|
+
ChartArtifact,
|
|
1447
|
+
DEFAULT_CHART_ARTIFACT_LABELS,
|
|
1448
|
+
DEFAULT_TABLE_ARTIFACT_LABELS,
|
|
723
1449
|
TableArtifact,
|
|
724
1450
|
artifactToClipboardText,
|
|
725
1451
|
artifactTypes,
|