@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.
Files changed (56) hide show
  1. package/README.md +60 -0
  2. package/dist/artifacts.cjs +858 -132
  3. package/dist/artifacts.cjs.map +1 -1
  4. package/dist/artifacts.d.cts +38 -5
  5. package/dist/artifacts.d.ts +38 -5
  6. package/dist/artifacts.js +13 -3
  7. package/dist/blocks.cjs +1185 -467
  8. package/dist/blocks.cjs.map +1 -1
  9. package/dist/blocks.css +1 -1
  10. package/dist/blocks.d.cts +23 -10
  11. package/dist/blocks.d.ts +23 -10
  12. package/dist/blocks.js +6 -3
  13. package/dist/chart.cjs +594 -0
  14. package/dist/chart.cjs.map +1 -0
  15. package/dist/chart.d.cts +439 -0
  16. package/dist/chart.d.ts +439 -0
  17. package/dist/chart.js +32 -0
  18. package/dist/chat.cjs +1207 -489
  19. package/dist/chat.cjs.map +1 -1
  20. package/dist/chat.d.cts +3 -2
  21. package/dist/chat.d.ts +3 -2
  22. package/dist/chat.js +7 -4
  23. package/dist/{chunk-MXOPG747.js → chunk-36CB624W.js} +7 -7
  24. package/dist/chunk-36CB624W.js.map +1 -0
  25. package/dist/{chunk-L35M3OD5.js → chunk-4MZZH3WX.js} +5 -11
  26. package/dist/chunk-4MZZH3WX.js.map +1 -0
  27. package/dist/chunk-AQEJRMRN.js +1 -0
  28. package/dist/chunk-AQEJRMRN.js.map +1 -0
  29. package/dist/{chunk-OUW6PUEB.js → chunk-FFX3CAOX.js} +7 -7
  30. package/dist/chunk-P6GUNIAE.js +11 -0
  31. package/dist/chunk-P6GUNIAE.js.map +1 -0
  32. package/dist/chunk-QXGYMDIA.js +477 -0
  33. package/dist/chunk-QXGYMDIA.js.map +1 -0
  34. package/dist/{chunk-BX5TCEPR.js → chunk-Z5JWF24N.js} +388 -105
  35. package/dist/chunk-Z5JWF24N.js.map +1 -0
  36. package/dist/index.cjs +924 -198
  37. package/dist/index.cjs.map +1 -1
  38. package/dist/index.d.cts +4 -3
  39. package/dist/index.d.ts +4 -3
  40. package/dist/index.js +23 -15
  41. package/dist/table.js +2 -1
  42. package/dist/types-B2aYk82c.d.cts +29 -0
  43. package/dist/types-B2aYk82c.d.ts +29 -0
  44. package/dist/types-C0BvwliI.d.cts +332 -0
  45. package/dist/types-C5bFH4v3.d.ts +332 -0
  46. package/dist/{types-DuuRI4ll.d.cts → types-DCutaXjZ.d.cts} +16 -1
  47. package/dist/{types-C9XX-Uhk.d.ts → types-uPfn67Dc.d.ts} +16 -1
  48. package/package.json +13 -1
  49. package/dist/chunk-BX5TCEPR.js.map +0 -1
  50. package/dist/chunk-L35M3OD5.js.map +0 -1
  51. package/dist/chunk-MEJESPTZ.js +0 -1
  52. package/dist/chunk-MXOPG747.js.map +0 -1
  53. package/dist/types-3Gzk7cRt.d.cts +0 -121
  54. package/dist/types-3Gzk7cRt.d.ts +0 -121
  55. /package/dist/{chunk-MEJESPTZ.js.map → chart.js.map} +0 -0
  56. /package/dist/{chunk-OUW6PUEB.js.map → chunk-FFX3CAOX.js.map} +0 -0
package/dist/chat.cjs CHANGED
@@ -40,7 +40,7 @@ __export(chat_exports, {
40
40
  module.exports = __toCommonJS(chat_exports);
41
41
 
42
42
  // src/components/chat/chat-message/chat-message.tsx
43
- var import_react11 = require("react");
43
+ var import_react15 = require("react");
44
44
 
45
45
  // src/components/blocks/types.ts
46
46
  var blockTypes = {
@@ -48,13 +48,33 @@ var blockTypes = {
48
48
  ARTIFACT_REF: "ARTIFACT_REF"
49
49
  };
50
50
 
51
- // src/components/artifacts/types.ts
51
+ // src/components/artifacts/artifact-base.ts
52
52
  var artifactTypes = {
53
- TABLE: "TABLE"
53
+ TABLE: "TABLE",
54
+ CHART: "CHART"
54
55
  };
55
56
 
56
- // src/components/artifacts/table-artifact/table-artifact.tsx
57
- var import_react7 = require("react");
57
+ // src/components/artifacts/chart-artifact/types.ts
58
+ var DEFAULT_CHART_ARTIFACT_LABELS = {
59
+ download: "Download CSV",
60
+ unavailable: "Chart data is unavailable.",
61
+ loadError: "Couldn't load chart data.",
62
+ loading: "Loading chart\u2026",
63
+ empty: "No data to chart.",
64
+ othersCategoryLabels: ["Others", "Other"]
65
+ };
66
+
67
+ // src/components/artifacts/table-artifact/types.ts
68
+ var DEFAULT_TABLE_ARTIFACT_LABELS = {
69
+ download: "Download CSV",
70
+ unavailable: "Table data is unavailable.",
71
+ loadError: "Couldn't load table data.",
72
+ loading: "Loading table\u2026",
73
+ empty: "No rows to display."
74
+ };
75
+
76
+ // src/components/artifacts/chart-artifact/chart-artifact.tsx
77
+ var import_react9 = require("react");
58
78
 
59
79
  // src/lib/utils.ts
60
80
  var import_clsx = require("clsx");
@@ -394,7 +414,12 @@ TooltipContent.displayName = TooltipPrimitive.Content.displayName;
394
414
 
395
415
  // src/components/artifacts/artifact-download-button/artifact-download-button.tsx
396
416
  var import_jsx_runtime5 = require("react/jsx-runtime");
397
- function ArtifactDownloadButton({ href }) {
417
+ var DEFAULT_LABEL = "Download CSV";
418
+ function ArtifactDownloadButton({
419
+ href,
420
+ label = DEFAULT_LABEL,
421
+ onClick
422
+ }) {
398
423
  if (!isSafeDownloadUrl(href)) return null;
399
424
  return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(TooltipProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Tooltip, { children: [
400
425
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
@@ -404,12 +429,13 @@ function ArtifactDownloadButton({ href }) {
404
429
  href,
405
430
  download: true,
406
431
  rel: "noopener noreferrer",
432
+ onClick,
407
433
  icon: IconDownload,
408
434
  size: "small",
409
- "aria-label": "Download artifact"
435
+ "aria-label": label
410
436
  }
411
437
  ) }),
412
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(TooltipContent, { children: "Download" })
438
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(TooltipContent, { children: label })
413
439
  ] }) });
414
440
  }
415
441
  function isSafeDownloadUrl(url) {
@@ -421,87 +447,10 @@ function isSafeDownloadUrl(url) {
421
447
  }
422
448
  }
423
449
 
424
- // src/components/ui/table.tsx
425
- var React2 = __toESM(require("react"), 1);
426
- var import_jsx_runtime6 = require("react/jsx-runtime");
427
- var Table = React2.forwardRef(({ className, containerClassName, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: cn("relative w-full overflow-auto", containerClassName), children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("table", { ref, className: cn("w-full caption-bottom text-sm", className), ...props }) }));
428
- Table.displayName = "Table";
429
- var TableHeader = React2.forwardRef(
430
- ({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("thead", { ref, className: cn("[&_tr]:border-b", className), ...props })
431
- );
432
- TableHeader.displayName = "TableHeader";
433
- var TableBody = React2.forwardRef(
434
- ({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("tbody", { ref, className: cn("[&_tr:last-child]:border-0", className), ...props })
435
- );
436
- TableBody.displayName = "TableBody";
437
- var TableFooter = React2.forwardRef(
438
- ({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("tfoot", { ref, className: cn("border-t bg-muted/50 font-medium [&>tr]:last:border-b-0", className), ...props })
439
- );
440
- TableFooter.displayName = "TableFooter";
441
- var TableRow = React2.forwardRef(
442
- ({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
443
- "tr",
444
- {
445
- ref,
446
- className: cn("border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted", className),
447
- ...props
448
- }
449
- )
450
- );
451
- TableRow.displayName = "TableRow";
452
- var TableHead = React2.forwardRef(
453
- ({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
454
- "th",
455
- {
456
- ref,
457
- className: cn(
458
- "h-12 px-4 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0",
459
- className
460
- ),
461
- ...props
462
- }
463
- )
464
- );
465
- TableHead.displayName = "TableHead";
466
- var TableCell = React2.forwardRef(
467
- ({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("td", { ref, className: cn("p-4 align-middle [&:has([role=checkbox])]:pr-0", className), ...props })
468
- );
469
- TableCell.displayName = "TableCell";
470
- var TableCaption = React2.forwardRef(
471
- ({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("caption", { ref, className: cn("mt-4 text-sm text-muted-foreground", className), ...props })
472
- );
473
- TableCaption.displayName = "TableCaption";
474
-
475
- // src/components/table/use-scroll-shadow.ts
450
+ // src/components/artifacts/status-message.tsx
476
451
  var import_react4 = require("react");
477
- function useScrollShadow() {
478
- const targetRef = (0, import_react4.useRef)(null);
479
- const [isScrolled, setIsScrolled] = (0, import_react4.useState)(false);
480
- (0, import_react4.useEffect)(() => {
481
- const target = targetRef.current;
482
- if (!target) return;
483
- let scrollContainer = target;
484
- while (scrollContainer) {
485
- const overflowY = getComputedStyle(scrollContainer).overflowY;
486
- if (overflowY === "auto" || overflowY === "scroll") break;
487
- scrollContainer = scrollContainer.parentElement;
488
- }
489
- if (!scrollContainer) return;
490
- setIsScrolled(scrollContainer.scrollTop > 0);
491
- const handleScroll = () => {
492
- if (!scrollContainer) return;
493
- setIsScrolled(scrollContainer.scrollTop > 0);
494
- };
495
- scrollContainer.addEventListener("scroll", handleScroll, { passive: true });
496
- return () => scrollContainer.removeEventListener("scroll", handleScroll);
497
- }, []);
498
- return { targetRef, isScrolled };
499
- }
500
-
501
- // src/components/artifacts/table-artifact/table-artifact-styles.ts
502
- var import_react5 = require("react");
503
452
  function StatusMessage({ $tone, children }) {
504
- return (0, import_react5.createElement)(
453
+ return (0, import_react4.createElement)(
505
454
  "div",
506
455
  {
507
456
  className: `py-6 px-2 text-center text-sm font-medium leading-5 ${$tone === "error" ? "text-red-600" : "text-brown-70"}`
@@ -510,313 +459,80 @@ function StatusMessage({ $tone, children }) {
510
459
  );
511
460
  }
512
461
 
513
- // src/components/artifacts/table-artifact/table-artifact-content.constants.ts
514
- var TABLE_STATUS = {
515
- LOADING: "loading",
516
- READY: "ready",
517
- ERROR: "error",
518
- UNAVAILABLE: "unavailable"
519
- };
520
-
521
- // src/components/artifacts/table-artifact/table-artifact-content.utils.ts
522
- function formatCell(row, column) {
523
- return row[column.key] ?? "";
524
- }
525
- var HEAD_CLASS = "h-auto py-2 px-4 text-xs font-medium text-foreground whitespace-nowrap text-left";
526
- var CELL_CLASS = "p-4 text-sm font-normal text-foreground align-top min-w-[150px] text-left";
527
- var ROW_CLASSES = "border-b-brown-30 bg-background/70 hover:bg-secondary";
528
- var DATA_ROW_CLASSES = `${ROW_CLASSES} animate-in fade-in duration-300`;
529
- var SCROLL_SHADOW = "0 1px 2px rgb(0 0 0 / 0.01), 0 6px 12px -2px rgb(0 0 0 / 0.03)";
462
+ // src/components/chart/bar-chart/bar-chart.tsx
463
+ var import_react5 = require("react");
464
+ var import_recharts2 = require("recharts");
530
465
 
531
- // src/components/artifacts/table-artifact/table-artifact-content.tsx
532
- var import_jsx_runtime7 = require("react/jsx-runtime");
533
- function TableArtifactContent({
534
- columns,
535
- data,
536
- knownRowCount,
537
- maxShimmerRows,
538
- maxBodyHeight,
539
- unavailableMessage
466
+ // src/components/chart/chart-container.tsx
467
+ var import_recharts = require("recharts");
468
+ var import_jsx_runtime6 = require("react/jsx-runtime");
469
+ function ChartContainer({
470
+ children,
471
+ height = 320,
472
+ width = "100%",
473
+ className,
474
+ style
540
475
  }) {
541
- if (data.status === TABLE_STATUS.ERROR) {
542
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(StatusMessage, { $tone: "error", children: "Couldn't load table data." });
543
- }
544
- if (data.status === TABLE_STATUS.UNAVAILABLE) {
545
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(StatusMessage, { $tone: "info", children: unavailableMessage });
546
- }
547
- if (data.status === TABLE_STATUS.LOADING) {
548
- if (typeof knownRowCount === "number" && knownRowCount > 0) {
549
- const shimmerCount = Math.min(knownRowCount, maxShimmerRows);
550
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ArtifactTable, { columns, maxBodyHeight, children: renderShimmerRows(columns, shimmerCount) });
551
- }
552
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(StatusMessage, { $tone: "info", children: "Loading table\u2026" });
553
- }
554
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ArtifactTable, { columns, maxBodyHeight, children: data.rows.length === 0 ? renderEmptyRow(columns.length) : renderDataRows(columns, data.rows) });
555
- }
556
- function ArtifactTable({ columns, maxBodyHeight, children }) {
557
- const { targetRef, isScrolled } = useScrollShadow();
558
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { ref: targetRef, className: "overscroll-none", style: { maxHeight: maxBodyHeight, overflowY: "auto" }, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(Table, { containerClassName: "overflow-visible", children: [
559
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
560
- TableHeader,
561
- {
562
- "data-scrolled": isScrolled ? "true" : void 0,
563
- className: "sticky top-0 z-10 bg-background/70 backdrop-blur-lg transition-shadow",
564
- style: isScrolled ? { boxShadow: SCROLL_SHADOW } : void 0,
565
- children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(TableRow, { className: "hover:bg-transparent", children: columns.map((column) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(TableHead, { scope: "col", className: HEAD_CLASS, children: column.label }, column.key)) })
566
- }
567
- ),
568
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(TableBody, { children })
569
- ] }) });
570
- }
571
- function renderShimmerRows(columns, count) {
572
- return Array.from({ length: count }).map((_, index) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(TableRow, { className: ROW_CLASSES, "aria-hidden": "true", children: columns.map((column) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(TableCell, { className: CELL_CLASS, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "block h-3 w-3/4 animate-pulse rounded bg-muted-foreground/20" }) }, column.key)) }, `shimmer-${index}`));
573
- }
574
- function renderDataRows(columns, rows) {
575
- return rows.map((row, index) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(TableRow, { className: DATA_ROW_CLASSES, children: columns.map((column) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(TableCell, { className: CELL_CLASS, children: formatCell(row, column) }, column.key)) }, index));
576
- }
577
- function renderEmptyRow(colSpan) {
578
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(TableRow, { className: "hover:bg-transparent", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(TableCell, { colSpan, className: "py-6 text-center text-sm text-muted-foreground", children: "No rows to display." }) });
476
+ 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 }) });
579
477
  }
580
478
 
581
- // src/components/artifacts/table-artifact/use-table-artifact-data.ts
582
- var import_react6 = require("react");
583
-
584
- // src/components/artifacts/table-artifact/parse-csv.ts
585
- var import_papaparse = __toESM(require("papaparse"), 1);
586
- function parseCsv(text, columns) {
587
- const result = import_papaparse.default.parse(text, {
588
- header: true,
589
- skipEmptyLines: true,
590
- dynamicTyping: false
591
- });
592
- const csvHeaders = new Set(result.meta?.fields ?? []);
593
- const presentColumns = columns.filter((col) => csvHeaders.has(col.key));
594
- return result.data.map((rawRow) => {
595
- const row = {};
596
- for (const column of presentColumns) {
597
- const value = rawRow[column.key];
598
- row[column.key] = value === void 0 || value === "" ? null : value;
479
+ // src/components/chart/chart-legend-content.tsx
480
+ var import_jsx_runtime7 = require("react/jsx-runtime");
481
+ function ChartLegendContent({ payload, className, iconType = "square" }) {
482
+ if (!payload || payload.length === 0) return null;
483
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
484
+ "ul",
485
+ {
486
+ className: cn("flex flex-wrap items-center justify-center gap-x-4 gap-y-2 text-xs text-brown-80", className),
487
+ children: payload.map((entry, i) => {
488
+ const swatchColor = typeof entry.color === "string" ? entry.color : "transparent";
489
+ const label = entry.value !== void 0 ? String(entry.value) : "";
490
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("li", { className: "flex items-center gap-1.5", children: [
491
+ 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 } }),
492
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: label })
493
+ ] }, `${label}-${i}`);
494
+ })
599
495
  }
600
- return row;
601
- });
602
- }
603
-
604
- // src/components/artifacts/table-artifact/use-table-artifact-data.ts
605
- var LOADING = { status: "loading", rows: null, error: null };
606
- var UNAVAILABLE = { status: "unavailable", rows: null, error: null };
607
- var INITIAL_INTERNAL = { cacheKey: null, data: LOADING };
608
- function columnsFingerprint(columns) {
609
- return columns.map((c) => `${c.key}:${c.type ?? "string"}`).join("|");
610
- }
611
- function useTableArtifactData(presignedUrl, columns) {
612
- const colsKey = (0, import_react6.useMemo)(() => columnsFingerprint(columns), [columns]);
613
- const cacheKey = presignedUrl ? `${presignedUrl}::${colsKey}` : null;
614
- const [state, setState] = (0, import_react6.useState)(INITIAL_INTERNAL);
615
- (0, import_react6.useEffect)(() => {
616
- if (!presignedUrl) return;
617
- const controller = new AbortController();
618
- fetch(presignedUrl, {
619
- signal: controller.signal,
620
- headers: { Accept: "text/csv" },
621
- // Presigned URLs are cross-origin S3 in production and don't need credentials. Explicit
622
- // omit so a same-origin URL (compromised payload, local dev) can't accidentally receive
623
- // session cookies or auth headers.
624
- credentials: "omit"
625
- }).then(async (response) => {
626
- if (!response.ok) throw new Error(`Failed to load table data (${response.status})`);
627
- return response.text();
628
- }).then((text) => {
629
- if (controller.signal.aborted) return;
630
- try {
631
- const rows = parseCsv(text, columns);
632
- setState({ cacheKey: `${presignedUrl}::${colsKey}`, data: { status: "ready", rows, error: null } });
633
- } catch (parseError) {
634
- console.error("[TableArtifact] CSV parse failed", parseError);
635
- const err = parseError instanceof Error ? parseError : new Error(String(parseError));
636
- setState({ cacheKey: `${presignedUrl}::${colsKey}`, data: { status: "error", rows: null, error: err } });
637
- }
638
- }).catch((error) => {
639
- if (controller.signal.aborted) return;
640
- console.error("[TableArtifact] Fetch failed", error);
641
- const err = error instanceof Error ? error : new Error(String(error));
642
- setState({ cacheKey: `${presignedUrl}::${colsKey}`, data: { status: "error", rows: null, error: err } });
643
- });
644
- return () => {
645
- controller.abort();
646
- };
647
- }, [presignedUrl, colsKey]);
648
- if (!presignedUrl) return UNAVAILABLE;
649
- if (state.cacheKey !== cacheKey) return LOADING;
650
- return state.data;
651
- }
652
-
653
- // src/components/artifacts/table-artifact/validate.ts
654
- function isValidTableConfig(config) {
655
- if (!config || typeof config !== "object") return false;
656
- const candidate = config;
657
- if (!Array.isArray(candidate.columns)) return false;
658
- if (candidate.columns.length === 0) return false;
659
- return candidate.columns.every(isValidTableColumn);
660
- }
661
- function isValidTableColumn(column) {
662
- if (!column || typeof column !== "object") return false;
663
- const candidate = column;
664
- if (typeof candidate.key !== "string" || candidate.key.length === 0) return false;
665
- if (typeof candidate.label !== "string") return false;
666
- return true;
496
+ );
667
497
  }
668
498
 
669
- // src/components/artifacts/table-artifact/table-artifact.tsx
499
+ // src/components/chart/chart-tooltip-content.tsx
670
500
  var import_jsx_runtime8 = require("react/jsx-runtime");
671
- var TITLE_CLASSES = "text-sm font-semibold text-foreground";
672
- var TABLE_MAX_BODY_HEIGHT = 480;
673
- var MAX_SHIMMER_ROWS = 10;
674
- var MSG_UNAVAILABLE = "Table data is unavailable.";
675
- function TableArtifact({ artifact }) {
676
- const validConfig = isValidTableConfig(artifact.config);
677
- const validColumns = validConfig ? artifact.config.columns : null;
678
- (0, import_react7.useEffect)(() => {
679
- if (validConfig) return;
680
- console.warn("[clay] TableArtifact: malformed `config` \u2014 skipping render.");
681
- }, [validConfig]);
682
- const data = useTableArtifactData(
683
- validConfig ? artifact.protectedAsset?.presignedUrl ?? null : null,
684
- validColumns ?? []
685
- );
686
- const presignedUrl = artifact.protectedAsset?.presignedUrl ?? null;
687
- if (!validConfig || !validColumns) {
688
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Artifact, { className: "shadow-none w-full rounded-2xl bg-background/70", children: [
689
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(ArtifactHeader, { className: "bg-background/70", children: [
690
- artifact.title ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ArtifactTitle, { className: TITLE_CLASSES, children: artifact.title }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", {}),
691
- presignedUrl ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ArtifactActions, { children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ArtifactDownloadButton, { href: presignedUrl }) }) : null
692
- ] }),
693
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ArtifactContent, { className: "p-0 overflow-auto", style: { maxHeight: TABLE_MAX_BODY_HEIGHT }, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(StatusMessage, { $tone: "info", children: MSG_UNAVAILABLE }) })
694
- ] });
695
- }
696
- return (
697
- // The table body owns its own scroll container (`ArtifactTable` inside `TableArtifactContent`).
698
- // Disable the outer overflow container so only one scroll context exists this prevents
699
- // double-scroll nesting and lets `useScrollShadow` find the correct scroll ancestor.
700
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Artifact, { className: "shadow-none w-full rounded-2xl bg-background/70", children: [
701
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(ArtifactHeader, { className: "bg-background/70", children: [
702
- artifact.title ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ArtifactTitle, { className: TITLE_CLASSES, children: artifact.title }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", {}),
703
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ArtifactActions, { children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ArtifactDownloadButton, { href: presignedUrl ?? "" }) })
704
- ] }),
705
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ArtifactContent, { className: "p-0 overflow-visible", style: { maxHeight: "none" }, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
706
- TableArtifactContent,
707
- {
708
- columns: validColumns,
709
- data,
710
- knownRowCount: artifact.config.stats?.rowCount,
711
- maxShimmerRows: MAX_SHIMMER_ROWS,
712
- maxBodyHeight: TABLE_MAX_BODY_HEIGHT,
713
- unavailableMessage: MSG_UNAVAILABLE
714
- }
715
- ) })
716
- ] })
501
+ function ChartTooltipContent({
502
+ active,
503
+ payload,
504
+ label,
505
+ className,
506
+ showLabel = true,
507
+ formatValue
508
+ }) {
509
+ if (!active || !payload || payload.length === 0) return null;
510
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
511
+ "div",
512
+ {
513
+ className: cn(
514
+ "rounded-lg border border-brown-40 bg-brown-10 px-3 py-2 text-xs shadow-sm",
515
+ "min-w-32 text-foreground",
516
+ className
517
+ ),
518
+ children: [
519
+ 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,
520
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("ul", { className: "space-y-1", children: payload.map((entry, i) => {
521
+ const name = entry.name !== void 0 ? String(entry.name) : "";
522
+ const rawValue = entry.value;
523
+ const display = formatValue ? formatValue(rawValue, name) : rawValue === void 0 ? "" : String(rawValue);
524
+ const swatch = typeof entry.color === "string" ? entry.color : "transparent";
525
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("li", { className: "flex items-center gap-2", children: [
526
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "inline-block h-2.5 w-2.5 shrink-0 rounded-sm", style: { background: swatch } }),
527
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "text-brown-70", children: name }),
528
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "ml-auto font-semibold tabular-nums text-brown-90", children: display })
529
+ ] }, `${name}-${i}`);
530
+ }) })
531
+ ]
532
+ }
717
533
  );
718
534
  }
719
535
 
720
- // src/components/artifacts/table-artifact/to-clipboard-text.ts
721
- function tableArtifactToClipboardText(artifact) {
722
- const trimmedTitle = artifact.title?.trim();
723
- return trimmedTitle || "Table";
724
- }
725
-
726
- // src/components/artifacts/artifact-services/table-artifact-service.ts
727
- var TableArtifactService = {
728
- type: artifactTypes.TABLE,
729
- Component: TableArtifact,
730
- toClipboardText: tableArtifactToClipboardText
731
- };
732
-
733
- // src/components/artifacts/artifact-services/index.ts
734
- var ArtifactServices = {
735
- [artifactTypes.TABLE]: TableArtifactService
736
- };
737
- function artifactToClipboardText(artifact) {
738
- const entry = ArtifactServices[artifact.type];
739
- if (!entry) {
740
- return `[Artifact: ${artifact.title ?? artifact.type}]`;
741
- }
742
- return entry.toClipboardText(artifact);
743
- }
744
-
745
- // src/components/blocks/artifact-ref-block/artifact-ref-block.tsx
746
- var import_jsx_runtime9 = require("react/jsx-runtime");
747
- function ArtifactRefBlock({ block }) {
748
- const { artifact } = block.payload;
749
- const entry = ArtifactServices[artifact.type];
750
- if (!entry) return null;
751
- const { Component } = entry;
752
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Component, { artifact });
753
- }
754
-
755
- // src/components/blocks/block-services/artifact-ref-block-service.ts
756
- var ArtifactRefBlockService = {
757
- type: blockTypes.ARTIFACT_REF,
758
- Component: ArtifactRefBlock,
759
- /**
760
- * Delegates to the registered artifact component's `toClipboardText`. Falls back to
761
- * `[Artifact: <title>]` via `artifactToClipboardText`'s own fallback when the artifact type
762
- * has no registered component, so copy never silently produces an empty string.
763
- */
764
- toClipboardText: (block) => artifactToClipboardText(block.payload.artifact)
765
- };
766
-
767
- // src/utils/markdown-to-plain-text.ts
768
- var PLACEHOLDER_PREFIX = "\u200B\u200BIC";
769
- var PLACEHOLDER_SUFFIX = "\u200B\u200B";
770
- var PLACEHOLDER_REGEX = new RegExp(`${PLACEHOLDER_PREFIX}(\\d+)${PLACEHOLDER_SUFFIX}`, "g");
771
- function stripInlineMarks(text) {
772
- return text.replace(/\*{3}(.+?)\*{3}/g, "$1").replace(/\*{2}(.+?)\*{2}/g, "$1").replace(/_{2}(.+?)_{2}/g, "$1").replace(/\*(.+?)\*/g, "$1").replace(/_(.+?)_/g, "$1").replace(/~~(.+?)~~/g, "$1");
773
- }
774
- function markdownToPlainText(markdown) {
775
- if (!markdown) return "";
776
- const inlineCodeSnippets = [];
777
- let result = markdown.replace(/^<!--[\s\S]*?--!?>\n?/gm, "").replace(/```[\s\S]*?```/g, "").replace(/`([^`]+)`/g, (_match, code) => {
778
- inlineCodeSnippets.push(code);
779
- return `${PLACEHOLDER_PREFIX}${inlineCodeSnippets.length - 1}${PLACEHOLDER_SUFFIX}`;
780
- }).replace(/^#{1,6}\s+/gm, "").replace(/^(?:---|\*\*\*|___)\s*$/gm, "");
781
- result = stripInlineMarks(result).replace(/!\[([^\]]*)\]\([^)]+\)/g, "$1").replace(/\[([^\]]+)\]\([^)]+\)/g, "$1").replace(/^>\s?/gm, "").replace(/^\|[-:\s|]+\|\s*$/gm, "").replace(/^\|\s?/gm, "").replace(/\s?\|$/gm, "").replace(/\n{3,}/g, "\n\n").trim();
782
- result = result.replace(PLACEHOLDER_REGEX, (_match, index) => inlineCodeSnippets[Number(index)]);
783
- return result;
784
- }
785
-
786
- // src/components/blocks/text-block/text-block.tsx
787
- var import_react_markdown = __toESM(require("react-markdown"), 1);
788
- var import_remark_breaks = __toESM(require("remark-breaks"), 1);
789
- var import_remark_gfm = __toESM(require("remark-gfm"), 1);
790
-
791
- // src/components/blocks/scrollable-table/scrollable-table-styles.ts
792
- var import_styled_components3 = __toESM(require("styled-components"), 1);
793
- var TableContainer = import_styled_components3.default.div`
794
- position: relative;
795
- margin-block: 8px 16px;
796
- `;
797
- var TableScroll = import_styled_components3.default.div`
798
- width: 100%;
799
- overflow-x: auto;
800
- overflow-y: hidden;
801
- `;
802
-
803
- // src/components/blocks/scrollable-table/scrollable-table.tsx
804
- var import_jsx_runtime10 = require("react/jsx-runtime");
805
- function ScrollableTable({ node: _node, ...props }) {
806
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(TableContainer, { children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(TableScroll, { children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("table", { ...props }) }) });
807
- }
808
-
809
- // src/components/blocks/text-block/sanitize.ts
810
- function sanitize(text) {
811
- return text.split("\n").filter((line) => {
812
- const t = line.trim();
813
- return !(t.startsWith("<!-- table-title:") && t.endsWith("-->"));
814
- }).join("\n");
815
- }
816
-
817
- // src/components/blocks/text-block/text-block-styles.ts
818
- var import_styled_components4 = __toESM(require("styled-components"), 1);
819
-
820
536
  // src/tokens/typography.ts
821
537
  var fontFamilies = {
822
538
  /** Geist — primary UI font for body text, labels, headings, and display. */
@@ -1112,106 +828,1108 @@ function typographyMixin(type) {
1112
828
  ].join(";\n ");
1113
829
  }
1114
830
 
1115
- // src/components/blocks/text-block/text-block-styles.ts
1116
- var TextBlockWrapper = import_styled_components4.default.div`
1117
- ${typographyMixin(typographyTypes.GEIST_BODY_S_REGULAR)};
1118
- color: ${colors["brown-100"]};
1119
- word-break: break-word;
1120
-
1121
- & h1 {
1122
- ${typographyMixin(typographyTypes.GEIST_HEADING_S_BOLD)};
1123
- margin-block: 20px;
831
+ // src/components/chart/chart-palette.ts
832
+ var CHART_PALETTE = [
833
+ colors["blue-600"],
834
+ // #2563EB — primary
835
+ colors["blue-400"],
836
+ // #60A5FA — mid
837
+ colors["blue-300"],
838
+ // #93C5FD — light
839
+ colors["blue-100"],
840
+ // #DBEAFE — very light
841
+ colors["indigo-400"],
842
+ // #818CF8
843
+ colors["indigo-500"],
844
+ // #6366F1
845
+ colors["indigo-600"],
846
+ // #4F46E5
847
+ colors["indigo-800"],
848
+ // #3730A3
849
+ colors["blue-800"]
850
+ // #1E40AF — deepest
851
+ ];
852
+ var OTHERS_SLICE_COLOR = colors["brown-60"];
853
+ var DEFAULT_OTHERS_CATEGORY_LABELS = ["others", "other"];
854
+ function isOthersCategory(value, labels) {
855
+ if (value === null || value === void 0) return false;
856
+ if (labels.length === 0) return false;
857
+ const normalized = String(value).trim().toLowerCase();
858
+ for (const label of labels) {
859
+ if (normalized === label.toLowerCase()) return true;
1124
860
  }
861
+ return false;
862
+ }
863
+ function colorForSeriesIndex(index, palette = CHART_PALETTE) {
864
+ return palette[index % palette.length] ?? CHART_PALETTE[0];
865
+ }
1125
866
 
1126
- & h2 {
1127
- ${typographyMixin(typographyTypes.GEIST_BODY_L_SEMI_BOLD)};
1128
- margin-block: 12px;
1129
- }
867
+ // src/components/chart/chart-styles.ts
868
+ var GRID_STROKE = colors["brown-40"];
869
+ var AXIS_STROKE = colors["brown-50"];
870
+ var AXIS_TICK_STYLE = {
871
+ fill: colors["brown-70"],
872
+ fontSize: 12,
873
+ fontWeight: 500
874
+ };
875
+ var CHART_VALUE_LABEL_STYLE = {
876
+ fill: colors["brown-70"],
877
+ fontSize: 12,
878
+ fontWeight: 500
879
+ };
880
+ var BAR_TOOLTIP_CURSOR_FILL = colors["brown-alpha-12"];
881
+ var LINE_ACTIVE_DOT_STROKE = colors["brown-10"];
882
+ var CHART_MARGIN = { top: 8, right: 16, left: 0, bottom: 4 };
883
+ var PIE_CHART_MARGIN = { top: 0, right: 24, left: 24, bottom: 4 };
884
+ var CHART_AXIS_PROPS = {
885
+ stroke: AXIS_STROKE,
886
+ tick: AXIS_TICK_STYLE,
887
+ tickLine: false,
888
+ axisLine: false
889
+ };
890
+ var CHART_GRID_PROPS = {
891
+ stroke: GRID_STROKE,
892
+ strokeDasharray: "3 3",
893
+ vertical: false
894
+ };
1130
895
 
1131
- & h3,
1132
- & h4,
1133
- & h5,
1134
- & h6 {
1135
- ${typographyMixin(typographyTypes.GEIST_BODY_M_SEMI_BOLD)};
1136
- margin-block: 8px;
896
+ // src/components/chart/chart-tick-utils.ts
897
+ function maxTickCharsByCategoryCount(n) {
898
+ if (n <= 5) return 24;
899
+ if (n <= 8) return 14;
900
+ if (n <= 12) return 10;
901
+ if (n <= 16) return 7;
902
+ return 5;
903
+ }
904
+ function truncateLabel(value, maxChars) {
905
+ const s = String(value ?? "");
906
+ if (s.length <= maxChars) return s;
907
+ return s.slice(0, Math.max(1, maxChars - 1)).trimEnd() + "\u2026";
908
+ }
909
+ function evenlyDistributedVisibleIndices(n, target) {
910
+ if (n <= target) {
911
+ return new Set(Array.from({ length: n }, (_, i) => i));
1137
912
  }
1138
-
1139
- & strong {
1140
- ${typographyMixin(typographyTypes.GEIST_BODY_S_SEMI_BOLD)};
913
+ const set = /* @__PURE__ */ new Set();
914
+ const step = (n - 1) / (target - 1);
915
+ for (let i = 0; i < target; i++) {
916
+ set.add(Math.round(i * step));
1141
917
  }
918
+ return set;
919
+ }
920
+ function defaultBarTickFormatter(categoryCount) {
921
+ const max = maxTickCharsByCategoryCount(categoryCount);
922
+ return (value) => truncateLabel(value, max);
923
+ }
924
+ function defaultLineChartTickFormatter(categoryCount, visibleIndices) {
925
+ const max = maxTickCharsByCategoryCount(visibleIndices.size || categoryCount);
926
+ return (value, index) => {
927
+ if (!visibleIndices.has(index)) return "";
928
+ return truncateLabel(value, max);
929
+ };
930
+ }
1142
931
 
1143
- & p {
1144
- margin-block: 0px;
1145
- }
932
+ // src/components/chart/coerce-numeric.ts
933
+ function coerceNumericColumns(rows, numericKeys) {
934
+ return rows.map((row) => {
935
+ const out = { ...row };
936
+ for (const k of numericKeys) {
937
+ const v = out[k];
938
+ if (v == null) continue;
939
+ const n = Number(v);
940
+ out[k] = Number.isFinite(n) ? n : NaN;
941
+ }
942
+ return out;
943
+ });
944
+ }
1146
945
 
1147
- & p + p {
1148
- margin-block: 8px 0px;
1149
- }
946
+ // src/components/chart/bar-chart/bar-chart.tsx
947
+ var import_jsx_runtime9 = require("react/jsx-runtime");
948
+ function BarChart({
949
+ data,
950
+ xKey,
951
+ series,
952
+ stacked = false,
953
+ height = 320,
954
+ width = "100%",
955
+ className,
956
+ palette = CHART_PALETTE,
957
+ showTooltip = true,
958
+ showLegend,
959
+ showGrid = true,
960
+ showXAxis = true,
961
+ showYAxis = true,
962
+ showValueLabels,
963
+ xAxisInterval = 0,
964
+ xAxisAngle,
965
+ xAxisTickFormatter
966
+ }) {
967
+ const seriesKeys = (0, import_react5.useMemo)(() => series.map((s) => s.key), [series]);
968
+ const coercedData = (0, import_react5.useMemo)(
969
+ () => coerceNumericColumns(data, seriesKeys),
970
+ [data, seriesKeys]
971
+ );
972
+ const resolvedShowLegend = showLegend ?? series.length > 1;
973
+ const resolvedShowValueLabels = showValueLabels ?? (!stacked && series.length === 1);
974
+ const resolvedAngle = xAxisAngle ?? (coercedData.length >= 8 ? -30 : 0);
975
+ const resolvedTickFormatter = (0, import_react5.useMemo)(
976
+ () => xAxisTickFormatter ?? defaultBarTickFormatter(coercedData.length),
977
+ [xAxisTickFormatter, coercedData.length]
978
+ );
979
+ const angledLabelPad = resolvedAngle === 0 ? 0 : Math.min(Math.abs(resolvedAngle) * 0.7, 40);
980
+ const chartMargin = { ...CHART_MARGIN, bottom: CHART_MARGIN.bottom + angledLabelPad };
981
+ 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: [
982
+ showGrid ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_recharts2.CartesianGrid, { ...CHART_GRID_PROPS }) : null,
983
+ showXAxis ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
984
+ import_recharts2.XAxis,
985
+ {
986
+ dataKey: xKey,
987
+ ...CHART_AXIS_PROPS,
988
+ interval: xAxisInterval,
989
+ angle: resolvedAngle,
990
+ textAnchor: resolvedAngle !== 0 ? "end" : "middle",
991
+ tickMargin: resolvedAngle === 0 ? 8 : 12,
992
+ height: resolvedAngle !== 0 ? 60 : void 0,
993
+ tickFormatter: resolvedTickFormatter
994
+ }
995
+ ) : null,
996
+ showYAxis ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_recharts2.YAxis, { ...CHART_AXIS_PROPS, width: 40 }) : null,
997
+ 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,
998
+ resolvedShowLegend ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
999
+ import_recharts2.Legend,
1000
+ {
1001
+ verticalAlign: "top",
1002
+ align: "center",
1003
+ wrapperStyle: { top: 0 },
1004
+ itemSorter: null,
1005
+ content: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ChartLegendContent, { iconType: "square" })
1006
+ }
1007
+ ) : null,
1008
+ series.map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1009
+ import_recharts2.Bar,
1010
+ {
1011
+ dataKey: s.key,
1012
+ name: s.label,
1013
+ fill: colorForSeriesIndex(i, palette),
1014
+ stackId: stacked ? "default" : s.key,
1015
+ radius: barRadius(stacked, i, series.length),
1016
+ children: resolvedShowValueLabels ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_recharts2.LabelList, { dataKey: s.key, position: "top", style: CHART_VALUE_LABEL_STYLE }) : null
1017
+ },
1018
+ s.key
1019
+ ))
1020
+ ] }) });
1021
+ }
1022
+ function barRadius(stacked, seriesIndex, seriesCount) {
1023
+ if (!stacked) return [6, 6, 0, 0];
1024
+ const isTopSegment = seriesIndex === seriesCount - 1;
1025
+ return isTopSegment ? [6, 6, 0, 0] : [0, 0, 0, 0];
1026
+ }
1150
1027
 
1151
- & ul,
1152
- & ol {
1153
- margin-block: 16px;
1154
- padding-left: 20px;
1155
- }
1028
+ // src/components/artifacts/chart-artifact/chart-variant-services/constants.ts
1029
+ var VARIANT_CHART_HEIGHT = "100%";
1156
1030
 
1157
- & li + li {
1158
- margin-block: 4px;
1159
- }
1031
+ // src/components/artifacts/chart-artifact/chart-variant-services/bar-chart-variant-component.tsx
1032
+ var import_jsx_runtime10 = require("react/jsx-runtime");
1033
+ function BarChartVariantComponent({ config, rows }) {
1034
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1035
+ BarChart,
1036
+ {
1037
+ data: rows,
1038
+ xKey: config.xKey,
1039
+ series: config.series,
1040
+ stacked: config.stacked,
1041
+ height: VARIANT_CHART_HEIGHT
1042
+ }
1043
+ );
1044
+ }
1160
1045
 
1161
- & ol {
1162
- list-style-type: decimal;
1163
- }
1046
+ // src/components/artifacts/chart-artifact/chart-variant-services/shared-column-keys.ts
1047
+ function seriesBasedColumnKeys(config) {
1048
+ return [config.xKey, ...config.series.map((s) => s.key)];
1049
+ }
1050
+ function pieColumnKeys(config) {
1051
+ return [config.categoryKey, config.valueKey];
1052
+ }
1164
1053
 
1165
- & ol ol {
1166
- list-style-type: lower-alpha;
1167
- }
1054
+ // src/components/artifacts/chart-artifact/chart-variant-services/shared-validators.ts
1055
+ function isValidChartSeries(series) {
1056
+ if (!series || typeof series !== "object") return false;
1057
+ const candidate = series;
1058
+ if (typeof candidate.key !== "string" || candidate.key.length === 0) return false;
1059
+ if (typeof candidate.label !== "string") return false;
1060
+ return true;
1061
+ }
1168
1062
 
1169
- & ol ol ol {
1170
- list-style-type: lower-roman;
1171
- }
1063
+ // src/components/artifacts/chart-artifact/chart-variant-services/bar-chart-variant-service.tsx
1064
+ function isValidBarChartConfig(config) {
1065
+ if (!config || typeof config !== "object") return false;
1066
+ const candidate = config;
1067
+ if (candidate.variant !== "BAR") return false;
1068
+ if (typeof candidate.xKey !== "string" || candidate.xKey.length === 0) return false;
1069
+ if (!Array.isArray(candidate.series) || candidate.series.length === 0) return false;
1070
+ if (!candidate.series.every(isValidChartSeries)) return false;
1071
+ return true;
1072
+ }
1073
+ var BarChartVariantService = {
1074
+ variant: "BAR",
1075
+ isValidConfig: isValidBarChartConfig,
1076
+ columnKeys: seriesBasedColumnKeys,
1077
+ Component: BarChartVariantComponent
1078
+ };
1172
1079
 
1173
- & a {
1174
- color: inherit;
1175
- text-decoration-line: underline;
1176
- text-decoration-style: dotted;
1177
- text-decoration-thickness: auto;
1178
- text-underline-offset: auto;
1179
- text-underline-position: from-font;
1180
- transition: color 75ms ease-in-out;
1080
+ // src/components/chart/line-chart/line-chart.tsx
1081
+ var import_react6 = require("react");
1082
+ var import_recharts3 = require("recharts");
1083
+ var import_jsx_runtime11 = require("react/jsx-runtime");
1084
+ var TARGET_VISIBLE_LABELS = 5;
1085
+ function LineChart({
1086
+ data,
1087
+ xKey,
1088
+ series,
1089
+ smooth = true,
1090
+ showDots = true,
1091
+ strokeWidth = 2,
1092
+ height = 320,
1093
+ width = "100%",
1094
+ className,
1095
+ palette = CHART_PALETTE,
1096
+ showTooltip = true,
1097
+ showLegend,
1098
+ showGrid = true,
1099
+ showXAxis = true,
1100
+ showYAxis = true,
1101
+ showValueLabels = false,
1102
+ xAxisInterval = 0,
1103
+ xAxisAngle = 0,
1104
+ xAxisTickFormatter
1105
+ }) {
1106
+ const seriesKeys = (0, import_react6.useMemo)(() => series.map((s) => s.key), [series]);
1107
+ const coercedData = (0, import_react6.useMemo)(
1108
+ () => coerceNumericColumns(data, seriesKeys),
1109
+ [data, seriesKeys]
1110
+ );
1111
+ const lineType = smooth ? "monotone" : "linear";
1112
+ const resolvedShowLegend = showLegend ?? series.length > 1;
1113
+ const resolvedTickFormatter = (0, import_react6.useMemo)(() => {
1114
+ if (xAxisTickFormatter) return xAxisTickFormatter;
1115
+ const visible = evenlyDistributedVisibleIndices(coercedData.length, TARGET_VISIBLE_LABELS);
1116
+ return defaultLineChartTickFormatter(coercedData.length, visible);
1117
+ }, [xAxisTickFormatter, coercedData.length]);
1118
+ const angledLabelPad = xAxisAngle === 0 ? 0 : Math.min(Math.abs(xAxisAngle) * 0.7, 40);
1119
+ const chartMargin = { ...CHART_MARGIN, right: CHART_MARGIN.right + 16, bottom: CHART_MARGIN.bottom + angledLabelPad };
1120
+ 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: [
1121
+ showGrid ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_recharts3.CartesianGrid, { ...CHART_GRID_PROPS }) : null,
1122
+ showXAxis ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1123
+ import_recharts3.XAxis,
1124
+ {
1125
+ dataKey: xKey,
1126
+ ...CHART_AXIS_PROPS,
1127
+ interval: xAxisInterval,
1128
+ angle: xAxisAngle,
1129
+ textAnchor: xAxisAngle !== 0 ? "end" : "middle",
1130
+ tickMargin: xAxisAngle === 0 ? 8 : 12,
1131
+ height: xAxisAngle !== 0 ? 60 : void 0,
1132
+ tickFormatter: resolvedTickFormatter
1133
+ }
1134
+ ) : null,
1135
+ showYAxis ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_recharts3.YAxis, { ...CHART_AXIS_PROPS, width: 40 }) : null,
1136
+ showTooltip ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_recharts3.Tooltip, { content: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ChartTooltipContent, {}) }) : null,
1137
+ resolvedShowLegend ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1138
+ import_recharts3.Legend,
1139
+ {
1140
+ verticalAlign: "top",
1141
+ align: "center",
1142
+ wrapperStyle: { top: 0 },
1143
+ itemSorter: null,
1144
+ content: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ChartLegendContent, { iconType: "line" })
1145
+ }
1146
+ ) : null,
1147
+ series.map((s, i) => {
1148
+ const color = colorForSeriesIndex(i, palette);
1149
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1150
+ import_recharts3.Line,
1151
+ {
1152
+ type: lineType,
1153
+ dataKey: s.key,
1154
+ name: s.label,
1155
+ stroke: color,
1156
+ strokeWidth,
1157
+ dot: showDots ? { r: 3, fill: color, stroke: color, strokeWidth: 0 } : false,
1158
+ activeDot: { r: 5, fill: color, stroke: LINE_ACTIVE_DOT_STROKE, strokeWidth: 2 },
1159
+ children: showValueLabels ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_recharts3.LabelList, { dataKey: s.key, position: "top", style: CHART_VALUE_LABEL_STYLE }) : null
1160
+ },
1161
+ s.key
1162
+ );
1163
+ })
1164
+ ] }) });
1165
+ }
1181
1166
 
1182
- &:hover {
1183
- color: ${colors["blue-600"]};
1167
+ // src/components/artifacts/chart-artifact/chart-variant-services/line-chart-variant-component.tsx
1168
+ var import_jsx_runtime12 = require("react/jsx-runtime");
1169
+ function LineChartVariantComponent({ config, rows }) {
1170
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(LineChart, { data: rows, xKey: config.xKey, series: config.series, height: VARIANT_CHART_HEIGHT });
1171
+ }
1172
+
1173
+ // src/components/artifacts/chart-artifact/chart-variant-services/line-chart-variant-service.tsx
1174
+ function isValidLineChartConfig(config) {
1175
+ if (!config || typeof config !== "object") return false;
1176
+ const candidate = config;
1177
+ if (candidate.variant !== "LINE") return false;
1178
+ if (typeof candidate.xKey !== "string" || candidate.xKey.length === 0) return false;
1179
+ if (!Array.isArray(candidate.series) || candidate.series.length === 0) return false;
1180
+ if (!candidate.series.every(isValidChartSeries)) return false;
1181
+ return true;
1182
+ }
1183
+ var LineChartVariantService = {
1184
+ variant: "LINE",
1185
+ isValidConfig: isValidLineChartConfig,
1186
+ columnKeys: seriesBasedColumnKeys,
1187
+ Component: LineChartVariantComponent
1188
+ };
1189
+
1190
+ // src/components/chart/pie-chart/pie-chart.tsx
1191
+ var import_react7 = require("react");
1192
+ var import_recharts4 = require("recharts");
1193
+ var import_jsx_runtime13 = require("react/jsx-runtime");
1194
+ function PieChart({
1195
+ data,
1196
+ categoryKey,
1197
+ valueKey,
1198
+ height = 320,
1199
+ width = "100%",
1200
+ className,
1201
+ palette = CHART_PALETTE,
1202
+ showLabels = true,
1203
+ showTooltip = true,
1204
+ showLegend = true,
1205
+ innerRadius = 0,
1206
+ outerRadius = "68%",
1207
+ othersCategoryLabels = DEFAULT_OTHERS_CATEGORY_LABELS
1208
+ }) {
1209
+ const { orderedData, sliceColors } = (0, import_react7.useMemo)(() => {
1210
+ const coerced = coerceNumericColumns(data, [valueKey]);
1211
+ const namedRows = [];
1212
+ const namedColors = [];
1213
+ const othersRows = [];
1214
+ const othersColors = [];
1215
+ for (const row of coerced) {
1216
+ if (isOthersCategory(row[categoryKey], othersCategoryLabels)) {
1217
+ othersRows.push(row);
1218
+ othersColors.push(OTHERS_SLICE_COLOR);
1219
+ } else {
1220
+ namedRows.push(row);
1221
+ namedColors.push(colorForSeriesIndex(namedRows.length - 1, palette));
1222
+ }
1184
1223
  }
1185
- }
1224
+ return {
1225
+ orderedData: [...namedRows, ...othersRows],
1226
+ sliceColors: [...namedColors, ...othersColors]
1227
+ };
1228
+ }, [data, valueKey, categoryKey, othersCategoryLabels, palette]);
1229
+ 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: [
1230
+ showTooltip ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_recharts4.Tooltip, { content: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ChartTooltipContent, {}) }) : null,
1231
+ showLegend ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1232
+ import_recharts4.Legend,
1233
+ {
1234
+ verticalAlign: "top",
1235
+ align: "center",
1236
+ wrapperStyle: { top: 0 },
1237
+ itemSorter: null,
1238
+ content: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ChartLegendContent, { iconType: "square" })
1239
+ }
1240
+ ) : null,
1241
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1242
+ import_recharts4.Pie,
1243
+ {
1244
+ data: orderedData,
1245
+ dataKey: valueKey,
1246
+ nameKey: categoryKey,
1247
+ cx: "50%",
1248
+ cy: "52%",
1249
+ startAngle: 90,
1250
+ endAngle: -270,
1251
+ innerRadius,
1252
+ outerRadius,
1253
+ label: showLabels ? (entry) => {
1254
+ const v = entry.value;
1255
+ if (v === void 0 || v === null) return null;
1256
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1257
+ "text",
1258
+ {
1259
+ x: entry.x,
1260
+ y: entry.y,
1261
+ fill: CHART_VALUE_LABEL_STYLE.fill,
1262
+ fontSize: CHART_VALUE_LABEL_STYLE.fontSize,
1263
+ fontWeight: CHART_VALUE_LABEL_STYLE.fontWeight,
1264
+ textAnchor: entry.textAnchor,
1265
+ dominantBaseline: "central",
1266
+ children: String(v)
1267
+ }
1268
+ );
1269
+ } : false,
1270
+ labelLine: false,
1271
+ stroke: "none",
1272
+ strokeWidth: 0,
1273
+ children: orderedData.map((_row, i) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_recharts4.Cell, { fill: sliceColors[i] }, i))
1274
+ }
1275
+ )
1276
+ ] }) });
1277
+ }
1186
1278
 
1187
- & hr {
1188
- border: none;
1189
- border-top: 1px solid ${colors["brown-40"]};
1190
- margin-block: 20px;
1191
- }
1279
+ // src/components/artifacts/chart-artifact/chart-variant-services/pie-chart-variant-component.tsx
1280
+ var import_jsx_runtime14 = require("react/jsx-runtime");
1281
+ function PieChartVariantComponent({
1282
+ config,
1283
+ rows,
1284
+ labels
1285
+ }) {
1286
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1287
+ PieChart,
1288
+ {
1289
+ data: rows,
1290
+ categoryKey: config.categoryKey,
1291
+ valueKey: config.valueKey,
1292
+ othersCategoryLabels: labels.othersCategoryLabels,
1293
+ height: VARIANT_CHART_HEIGHT
1294
+ }
1295
+ );
1296
+ }
1192
1297
 
1193
- & table {
1194
- margin: 0;
1195
- width: 100%;
1196
- border-collapse: collapse;
1197
- }
1298
+ // src/components/artifacts/chart-artifact/chart-variant-services/pie-chart-variant-service.tsx
1299
+ function isValidPieChartConfig(config) {
1300
+ if (!config || typeof config !== "object") return false;
1301
+ const candidate = config;
1302
+ if (candidate.variant !== "PIE") return false;
1303
+ if (typeof candidate.categoryKey !== "string" || candidate.categoryKey.length === 0) return false;
1304
+ if (typeof candidate.valueKey !== "string" || candidate.valueKey.length === 0) return false;
1305
+ if (!Array.isArray(candidate.series)) return false;
1306
+ return true;
1307
+ }
1308
+ var PieChartVariantService = {
1309
+ variant: "PIE",
1310
+ isValidConfig: isValidPieChartConfig,
1311
+ columnKeys: pieColumnKeys,
1312
+ Component: PieChartVariantComponent
1313
+ };
1198
1314
 
1199
- & th,
1200
- & td {
1201
- border: none;
1202
- border-bottom: 1px solid ${colors["brown-40"]};
1203
- text-align: left;
1204
- }
1315
+ // src/components/artifacts/chart-artifact/chart-variant-services/index.ts
1316
+ var ChartVariantServices = {
1317
+ LINE: LineChartVariantService,
1318
+ BAR: BarChartVariantService,
1319
+ PIE: PieChartVariantService
1320
+ };
1205
1321
 
1206
- & th {
1207
- ${typographyMixin(typographyTypes.GEIST_LABEL_CAPTION_MEDIUM)};
1208
- padding: 8px;
1322
+ // src/components/artifacts/chart-artifact/chart-artifact-content.tsx
1323
+ var import_jsx_runtime15 = require("react/jsx-runtime");
1324
+ function ChartArtifactContent({ config, rows, labels }) {
1325
+ const service = ChartVariantServices[config.variant];
1326
+ if (!service) {
1327
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(StatusMessage, { $tone: "info", children: labels.unavailable });
1209
1328
  }
1329
+ const Component = service.Component;
1330
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Component, { config, rows, labels });
1331
+ }
1210
1332
 
1211
- & td {
1212
- ${typographyMixin(typographyTypes.GEIST_BODY_XS_MEDIUM)};
1213
- padding: 16px 8px;
1214
- min-width: 150px;
1333
+ // src/components/artifacts/parse-csv.ts
1334
+ var import_papaparse = __toESM(require("papaparse"), 1);
1335
+ function parseCsv(text, columns) {
1336
+ const result = import_papaparse.default.parse(text, {
1337
+ header: true,
1338
+ skipEmptyLines: true,
1339
+ dynamicTyping: false
1340
+ });
1341
+ const csvHeaders = new Set(result.meta?.fields ?? []);
1342
+ const presentColumns = columns.filter((col) => csvHeaders.has(col.key));
1343
+ if (presentColumns.length === 0) return [];
1344
+ return result.data.map((rawRow) => {
1345
+ const row = {};
1346
+ for (const column of presentColumns) {
1347
+ const value = rawRow[column.key];
1348
+ row[column.key] = value === void 0 || value === "" ? null : value;
1349
+ }
1350
+ return row;
1351
+ });
1352
+ }
1353
+
1354
+ // src/components/artifacts/use-rows-artifact-data.ts
1355
+ var import_react8 = require("react");
1356
+ var LOADING = { status: "loading", rows: null, error: null };
1357
+ var UNAVAILABLE = { status: "unavailable", rows: null, error: null };
1358
+ var INITIAL_INTERNAL = { cacheKey: null, data: LOADING };
1359
+ function columnsFingerprint(columns) {
1360
+ return columns.map((c) => c.key).join("|");
1361
+ }
1362
+ function scrubUrl(url) {
1363
+ const q = url.indexOf("?");
1364
+ return q === -1 ? url : `${url.slice(0, q)}?<redacted>`;
1365
+ }
1366
+ function useRowsArtifactData(presignedUrl, columns, parse) {
1367
+ const colsKey = (0, import_react8.useMemo)(() => columnsFingerprint(columns), [columns]);
1368
+ const safeUrl = presignedUrl && isSafeDownloadUrl(presignedUrl) ? presignedUrl : null;
1369
+ const cacheKey = safeUrl ? `${safeUrl}::${colsKey}` : null;
1370
+ const [state, setState] = (0, import_react8.useState)(INITIAL_INTERNAL);
1371
+ (0, import_react8.useEffect)(() => {
1372
+ if (!safeUrl) return;
1373
+ const controller = new AbortController();
1374
+ fetch(safeUrl, {
1375
+ signal: controller.signal,
1376
+ headers: { Accept: "text/csv" },
1377
+ // Presigned URLs are cross-origin S3 in production and don't need credentials. Explicit
1378
+ // omit so a same-origin URL (compromised payload, local dev) can't accidentally receive
1379
+ // session cookies or auth headers.
1380
+ credentials: "omit"
1381
+ }).then(async (response) => {
1382
+ if (!response.ok) throw new Error(`Failed to load artifact data (${response.status})`);
1383
+ return response.text();
1384
+ }).then((text) => {
1385
+ if (controller.signal.aborted) return;
1386
+ try {
1387
+ const rows = parse(text, columns);
1388
+ setState({ cacheKey: `${safeUrl}::${colsKey}`, data: { status: "ready", rows, error: null } });
1389
+ } catch (parseError) {
1390
+ const err = parseError instanceof Error ? parseError : new Error(String(parseError));
1391
+ console.error("[ArtifactData] CSV parse failed:", err.message);
1392
+ setState({ cacheKey: `${safeUrl}::${colsKey}`, data: { status: "error", rows: null, error: err } });
1393
+ }
1394
+ }).catch((error) => {
1395
+ if (controller.signal.aborted) return;
1396
+ const err = error instanceof Error ? error : new Error(String(error));
1397
+ console.error(`[ArtifactData] Fetch failed [${scrubUrl(safeUrl)}]:`, err.message);
1398
+ setState({ cacheKey: `${safeUrl}::${colsKey}`, data: { status: "error", rows: null, error: err } });
1399
+ });
1400
+ return () => {
1401
+ controller.abort();
1402
+ };
1403
+ }, [safeUrl, colsKey]);
1404
+ if (!safeUrl) return UNAVAILABLE;
1405
+ if (state.cacheKey !== cacheKey) return LOADING;
1406
+ return state.data;
1407
+ }
1408
+
1409
+ // src/components/artifacts/chart-artifact/use-chart-artifact-data.ts
1410
+ function useChartArtifactData(presignedUrl, config) {
1411
+ const service = ChartVariantServices[config.variant];
1412
+ const keys = service ? service.columnKeys(config) : [];
1413
+ const columns = keys.map((key) => ({ key }));
1414
+ return useRowsArtifactData(presignedUrl, columns, parseCsv);
1415
+ }
1416
+
1417
+ // src/components/artifacts/chart-artifact/validate.ts
1418
+ function isValidChartConfig(config) {
1419
+ if (!config || typeof config !== "object") return false;
1420
+ const variant = config.variant;
1421
+ if (typeof variant !== "string") return false;
1422
+ const service = ChartVariantServices[variant];
1423
+ if (!service) return false;
1424
+ return service.isValidConfig(config);
1425
+ }
1426
+
1427
+ // src/components/artifacts/chart-artifact/chart-artifact.tsx
1428
+ var import_jsx_runtime16 = require("react/jsx-runtime");
1429
+ var TITLE_CLASSES = "text-sm font-semibold text-foreground";
1430
+ var CHART_HEIGHT = 320;
1431
+ function ChartArtifact({ artifact, labels, callbacks }) {
1432
+ const validConfig = isValidChartConfig(artifact.config);
1433
+ (0, import_react9.useEffect)(() => {
1434
+ if (validConfig) return;
1435
+ console.warn("[clay] ChartArtifact: malformed `config` \u2014 skipping render.");
1436
+ }, [validConfig]);
1437
+ const presignedUrl = artifact.protectedAsset?.presignedUrl ?? null;
1438
+ const resolvedLabels = (0, import_react9.useMemo)(
1439
+ () => ({ ...DEFAULT_CHART_ARTIFACT_LABELS, ...labels?.[artifactTypes.CHART] ?? {} }),
1440
+ [labels]
1441
+ );
1442
+ const data = useChartArtifactData(
1443
+ validConfig ? presignedUrl : null,
1444
+ validConfig ? artifact.config : { variant: "LINE", series: [], xKey: "x" }
1445
+ );
1446
+ const onDownload = callbacks?.[artifactTypes.CHART]?.onDownload;
1447
+ const handleDownloadClick = onDownload ? () => onDownload(artifact) : void 0;
1448
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(Artifact, { className: "shadow-none w-full rounded-2xl bg-background/70", children: [
1449
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(ArtifactHeader, { className: "bg-background/70", children: [
1450
+ artifact.title ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(ArtifactTitle, { className: TITLE_CLASSES, children: artifact.title }) : /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", {}),
1451
+ presignedUrl ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(ArtifactActions, { children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(ArtifactDownloadButton, { href: presignedUrl, label: resolvedLabels.download, onClick: handleDownloadClick }) }) : null
1452
+ ] }),
1453
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
1454
+ ArtifactContent,
1455
+ {
1456
+ className: "p-4 overflow-visible flex flex-none items-center justify-center",
1457
+ style: { height: CHART_HEIGHT },
1458
+ children: renderBody(validConfig, artifact, data, resolvedLabels)
1459
+ }
1460
+ )
1461
+ ] });
1462
+ }
1463
+ function renderBody(validConfig, artifact, data, labels) {
1464
+ if (!validConfig) {
1465
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(StatusMessage, { $tone: "info", children: labels.unavailable });
1466
+ }
1467
+ if (data.status === "error") {
1468
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(StatusMessage, { $tone: "error", children: labels.loadError });
1469
+ }
1470
+ if (data.status === "unavailable") {
1471
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(StatusMessage, { $tone: "info", children: labels.unavailable });
1472
+ }
1473
+ if (data.status === "loading") {
1474
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(StatusMessage, { $tone: "info", children: labels.loading });
1475
+ }
1476
+ if (data.rows.length === 0) {
1477
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(StatusMessage, { $tone: "info", children: labels.empty });
1478
+ }
1479
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(ChartArtifactContent, { config: artifact.config, rows: data.rows, labels });
1480
+ }
1481
+
1482
+ // src/components/artifacts/chart-artifact/to-clipboard-text.ts
1483
+ function chartArtifactToClipboardText(artifact) {
1484
+ const trimmedTitle = artifact.title?.trim();
1485
+ return trimmedTitle || "Chart";
1486
+ }
1487
+
1488
+ // src/components/artifacts/artifact-services/chart-artifact-service.ts
1489
+ var ChartArtifactService = {
1490
+ type: artifactTypes.CHART,
1491
+ Component: ChartArtifact,
1492
+ toClipboardText: chartArtifactToClipboardText
1493
+ };
1494
+
1495
+ // src/components/artifacts/table-artifact/table-artifact.tsx
1496
+ var import_react11 = require("react");
1497
+
1498
+ // src/components/ui/table.tsx
1499
+ var React2 = __toESM(require("react"), 1);
1500
+ var import_jsx_runtime17 = require("react/jsx-runtime");
1501
+ 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 }) }));
1502
+ Table.displayName = "Table";
1503
+ var TableHeader = React2.forwardRef(
1504
+ ({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("thead", { ref, className: cn("[&_tr]:border-b", className), ...props })
1505
+ );
1506
+ TableHeader.displayName = "TableHeader";
1507
+ var TableBody = React2.forwardRef(
1508
+ ({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("tbody", { ref, className: cn("[&_tr:last-child]:border-0", className), ...props })
1509
+ );
1510
+ TableBody.displayName = "TableBody";
1511
+ var TableFooter = React2.forwardRef(
1512
+ ({ 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 })
1513
+ );
1514
+ TableFooter.displayName = "TableFooter";
1515
+ var TableRow = React2.forwardRef(
1516
+ ({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
1517
+ "tr",
1518
+ {
1519
+ ref,
1520
+ className: cn("border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted", className),
1521
+ ...props
1522
+ }
1523
+ )
1524
+ );
1525
+ TableRow.displayName = "TableRow";
1526
+ var TableHead = React2.forwardRef(
1527
+ ({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
1528
+ "th",
1529
+ {
1530
+ ref,
1531
+ className: cn(
1532
+ "h-12 px-4 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0",
1533
+ className
1534
+ ),
1535
+ ...props
1536
+ }
1537
+ )
1538
+ );
1539
+ TableHead.displayName = "TableHead";
1540
+ var TableCell = React2.forwardRef(
1541
+ ({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("td", { ref, className: cn("p-4 align-middle [&:has([role=checkbox])]:pr-0", className), ...props })
1542
+ );
1543
+ TableCell.displayName = "TableCell";
1544
+ var TableCaption = React2.forwardRef(
1545
+ ({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("caption", { ref, className: cn("mt-4 text-sm text-muted-foreground", className), ...props })
1546
+ );
1547
+ TableCaption.displayName = "TableCaption";
1548
+
1549
+ // src/components/table/use-scroll-shadow.ts
1550
+ var import_react10 = require("react");
1551
+ function useScrollShadow() {
1552
+ const targetRef = (0, import_react10.useRef)(null);
1553
+ const [isScrolled, setIsScrolled] = (0, import_react10.useState)(false);
1554
+ (0, import_react10.useEffect)(() => {
1555
+ const target = targetRef.current;
1556
+ if (!target) return;
1557
+ let scrollContainer = target;
1558
+ while (scrollContainer) {
1559
+ const overflowY = getComputedStyle(scrollContainer).overflowY;
1560
+ if (overflowY === "auto" || overflowY === "scroll") break;
1561
+ scrollContainer = scrollContainer.parentElement;
1562
+ }
1563
+ if (!scrollContainer) return;
1564
+ setIsScrolled(scrollContainer.scrollTop > 0);
1565
+ const handleScroll = () => {
1566
+ if (!scrollContainer) return;
1567
+ setIsScrolled(scrollContainer.scrollTop > 0);
1568
+ };
1569
+ scrollContainer.addEventListener("scroll", handleScroll, { passive: true });
1570
+ return () => scrollContainer.removeEventListener("scroll", handleScroll);
1571
+ }, []);
1572
+ return { targetRef, isScrolled };
1573
+ }
1574
+
1575
+ // src/components/artifacts/table-artifact/table-artifact-content.constants.ts
1576
+ var TABLE_STATUS = {
1577
+ LOADING: "loading",
1578
+ READY: "ready",
1579
+ ERROR: "error",
1580
+ UNAVAILABLE: "unavailable"
1581
+ };
1582
+
1583
+ // src/components/artifacts/table-artifact/table-artifact-content.utils.ts
1584
+ function formatCell(row, column) {
1585
+ return row[column.key] ?? "";
1586
+ }
1587
+ var HEAD_CLASS = "h-auto py-2 px-4 text-xs font-medium text-foreground whitespace-nowrap text-left";
1588
+ var CELL_CLASS = "p-4 text-sm font-normal text-foreground align-top min-w-[150px] text-left";
1589
+ var ROW_CLASSES = "border-b-brown-30 bg-background/70 hover:bg-secondary";
1590
+ var DATA_ROW_CLASSES = `${ROW_CLASSES} animate-in fade-in duration-300`;
1591
+ var SCROLL_SHADOW = "0 1px 2px rgb(0 0 0 / 0.01), 0 6px 12px -2px rgb(0 0 0 / 0.03)";
1592
+
1593
+ // src/components/artifacts/table-artifact/table-artifact-content.tsx
1594
+ var import_jsx_runtime18 = require("react/jsx-runtime");
1595
+ function TableArtifactContent({
1596
+ columns,
1597
+ data,
1598
+ knownRowCount,
1599
+ maxShimmerRows,
1600
+ maxBodyHeight,
1601
+ labels
1602
+ }) {
1603
+ if (data.status === TABLE_STATUS.ERROR) {
1604
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(StatusMessage, { $tone: "error", children: labels.loadError });
1605
+ }
1606
+ if (data.status === TABLE_STATUS.UNAVAILABLE) {
1607
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(StatusMessage, { $tone: "info", children: labels.unavailable });
1608
+ }
1609
+ if (data.status === TABLE_STATUS.LOADING) {
1610
+ if (typeof knownRowCount === "number" && knownRowCount > 0) {
1611
+ const shimmerCount = Math.min(knownRowCount, maxShimmerRows);
1612
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ArtifactTable, { columns, maxBodyHeight, children: renderShimmerRows(columns, shimmerCount) });
1613
+ }
1614
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(StatusMessage, { $tone: "info", children: labels.loading });
1615
+ }
1616
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ArtifactTable, { columns, maxBodyHeight, children: data.rows.length === 0 ? renderEmptyRow(columns.length, labels.empty) : renderDataRows(columns, data.rows) });
1617
+ }
1618
+ function ArtifactTable({ columns, maxBodyHeight, children }) {
1619
+ const { targetRef, isScrolled } = useScrollShadow();
1620
+ 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: [
1621
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
1622
+ TableHeader,
1623
+ {
1624
+ "data-scrolled": isScrolled ? "true" : void 0,
1625
+ className: "sticky top-0 z-10 bg-background/70 backdrop-blur-lg transition-shadow",
1626
+ style: isScrolled ? { boxShadow: SCROLL_SHADOW } : void 0,
1627
+ 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)) })
1628
+ }
1629
+ ),
1630
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(TableBody, { children })
1631
+ ] }) });
1632
+ }
1633
+ function renderShimmerRows(columns, count) {
1634
+ 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}`));
1635
+ }
1636
+ function renderDataRows(columns, rows) {
1637
+ 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));
1638
+ }
1639
+ function renderEmptyRow(colSpan, emptyMessage) {
1640
+ 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 }) });
1641
+ }
1642
+
1643
+ // src/components/artifacts/table-artifact/use-table-artifact-data.ts
1644
+ function useTableArtifactData(presignedUrl, columns) {
1645
+ return useRowsArtifactData(presignedUrl, columns, parseCsv);
1646
+ }
1647
+
1648
+ // src/components/artifacts/table-artifact/validate.ts
1649
+ function isValidTableConfig(config) {
1650
+ if (!config || typeof config !== "object") return false;
1651
+ const candidate = config;
1652
+ if (!Array.isArray(candidate.columns)) return false;
1653
+ if (candidate.columns.length === 0) return false;
1654
+ return candidate.columns.every(isValidTableColumn);
1655
+ }
1656
+ function isValidTableColumn(column) {
1657
+ if (!column || typeof column !== "object") return false;
1658
+ const candidate = column;
1659
+ if (typeof candidate.key !== "string" || candidate.key.length === 0) return false;
1660
+ if (typeof candidate.label !== "string") return false;
1661
+ return true;
1662
+ }
1663
+
1664
+ // src/components/artifacts/table-artifact/table-artifact.tsx
1665
+ var import_jsx_runtime19 = require("react/jsx-runtime");
1666
+ var TITLE_CLASSES2 = "text-sm font-semibold text-foreground";
1667
+ var TABLE_MAX_BODY_HEIGHT = 480;
1668
+ var MAX_SHIMMER_ROWS = 10;
1669
+ function TableArtifact({ artifact, labels, callbacks }) {
1670
+ const validConfig = isValidTableConfig(artifact.config);
1671
+ const validColumns = validConfig ? artifact.config.columns : null;
1672
+ (0, import_react11.useEffect)(() => {
1673
+ if (validConfig) return;
1674
+ console.warn("[clay] TableArtifact: malformed `config` \u2014 skipping render.");
1675
+ }, [validConfig]);
1676
+ const resolvedLabels = (0, import_react11.useMemo)(
1677
+ () => ({ ...DEFAULT_TABLE_ARTIFACT_LABELS, ...labels?.[artifactTypes.TABLE] ?? {} }),
1678
+ [labels]
1679
+ );
1680
+ const data = useTableArtifactData(
1681
+ validConfig ? artifact.protectedAsset?.presignedUrl ?? null : null,
1682
+ validColumns ?? []
1683
+ );
1684
+ const presignedUrl = artifact.protectedAsset?.presignedUrl ?? null;
1685
+ const onDownload = callbacks?.[artifactTypes.TABLE]?.onDownload;
1686
+ const handleDownloadClick = onDownload ? () => onDownload(artifact) : void 0;
1687
+ if (!validConfig || !validColumns) {
1688
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(Artifact, { className: "shadow-none w-full rounded-2xl bg-background/70", children: [
1689
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(ArtifactHeader, { className: "bg-background/70", children: [
1690
+ artifact.title ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ArtifactTitle, { className: TITLE_CLASSES2, children: artifact.title }) : /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", {}),
1691
+ presignedUrl ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ArtifactActions, { children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
1692
+ ArtifactDownloadButton,
1693
+ {
1694
+ href: presignedUrl,
1695
+ label: resolvedLabels.download,
1696
+ onClick: handleDownloadClick
1697
+ }
1698
+ ) }) : null
1699
+ ] }),
1700
+ /* @__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 }) })
1701
+ ] });
1702
+ }
1703
+ return (
1704
+ // The table body owns its own scroll container (`ArtifactTable` inside `TableArtifactContent`).
1705
+ // Disable the outer overflow container so only one scroll context exists — this prevents
1706
+ // double-scroll nesting and lets `useScrollShadow` find the correct scroll ancestor.
1707
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(Artifact, { className: "shadow-none w-full rounded-2xl bg-background/70", children: [
1708
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(ArtifactHeader, { className: "bg-background/70", children: [
1709
+ artifact.title ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ArtifactTitle, { className: TITLE_CLASSES2, children: artifact.title }) : /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", {}),
1710
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ArtifactActions, { children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
1711
+ ArtifactDownloadButton,
1712
+ {
1713
+ href: presignedUrl ?? "",
1714
+ label: resolvedLabels.download,
1715
+ onClick: handleDownloadClick
1716
+ }
1717
+ ) })
1718
+ ] }),
1719
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ArtifactContent, { className: "p-0 overflow-visible", style: { maxHeight: "none" }, children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
1720
+ TableArtifactContent,
1721
+ {
1722
+ columns: validColumns,
1723
+ data,
1724
+ knownRowCount: artifact.config.stats?.rowCount,
1725
+ maxShimmerRows: MAX_SHIMMER_ROWS,
1726
+ maxBodyHeight: TABLE_MAX_BODY_HEIGHT,
1727
+ labels: resolvedLabels
1728
+ }
1729
+ ) })
1730
+ ] })
1731
+ );
1732
+ }
1733
+
1734
+ // src/components/artifacts/table-artifact/to-clipboard-text.ts
1735
+ function tableArtifactToClipboardText(artifact) {
1736
+ const trimmedTitle = artifact.title?.trim();
1737
+ return trimmedTitle || "Table";
1738
+ }
1739
+
1740
+ // src/components/artifacts/artifact-services/table-artifact-service.ts
1741
+ var TableArtifactService = {
1742
+ type: artifactTypes.TABLE,
1743
+ Component: TableArtifact,
1744
+ toClipboardText: tableArtifactToClipboardText
1745
+ };
1746
+
1747
+ // src/components/artifacts/artifact-services/index.ts
1748
+ var ArtifactServices = {
1749
+ [artifactTypes.TABLE]: TableArtifactService,
1750
+ [artifactTypes.CHART]: ChartArtifactService
1751
+ };
1752
+ function artifactToClipboardText(artifact) {
1753
+ const entry = ArtifactServices[artifact.type];
1754
+ if (!entry) {
1755
+ return `[Artifact: ${artifact.title ?? artifact.type}]`;
1756
+ }
1757
+ return entry.toClipboardText(artifact);
1758
+ }
1759
+
1760
+ // src/components/blocks/artifact-ref-block/artifact-ref-block.tsx
1761
+ var import_jsx_runtime20 = require("react/jsx-runtime");
1762
+ function ArtifactRefBlock({ block }) {
1763
+ const { artifact, labels, callbacks } = block.payload;
1764
+ const entry = ArtifactServices[artifact.type];
1765
+ if (!entry) return null;
1766
+ const { Component } = entry;
1767
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Component, { artifact, labels, callbacks });
1768
+ }
1769
+
1770
+ // src/components/blocks/block-services/artifact-ref-block-service.ts
1771
+ var ArtifactRefBlockService = {
1772
+ type: blockTypes.ARTIFACT_REF,
1773
+ Component: ArtifactRefBlock,
1774
+ /**
1775
+ * Delegates to the registered artifact component's `toClipboardText`. Falls back to
1776
+ * `[Artifact: <title>]` via `artifactToClipboardText`'s own fallback when the artifact type
1777
+ * has no registered component, so copy never silently produces an empty string.
1778
+ */
1779
+ toClipboardText: (block) => artifactToClipboardText(block.payload.artifact)
1780
+ };
1781
+
1782
+ // src/utils/markdown-to-plain-text.ts
1783
+ var PLACEHOLDER_PREFIX = "\u200B\u200BIC";
1784
+ var PLACEHOLDER_SUFFIX = "\u200B\u200B";
1785
+ var PLACEHOLDER_REGEX = new RegExp(`${PLACEHOLDER_PREFIX}(\\d+)${PLACEHOLDER_SUFFIX}`, "g");
1786
+ function stripInlineMarks(text) {
1787
+ return text.replace(/\*{3}(.+?)\*{3}/g, "$1").replace(/\*{2}(.+?)\*{2}/g, "$1").replace(/_{2}(.+?)_{2}/g, "$1").replace(/\*(.+?)\*/g, "$1").replace(/_(.+?)_/g, "$1").replace(/~~(.+?)~~/g, "$1");
1788
+ }
1789
+ function markdownToPlainText(markdown) {
1790
+ if (!markdown) return "";
1791
+ const inlineCodeSnippets = [];
1792
+ let result = markdown.replace(/^<!--[\s\S]*?--!?>\n?/gm, "").replace(/```[\s\S]*?```/g, "").replace(/`([^`]+)`/g, (_match, code) => {
1793
+ inlineCodeSnippets.push(code);
1794
+ return `${PLACEHOLDER_PREFIX}${inlineCodeSnippets.length - 1}${PLACEHOLDER_SUFFIX}`;
1795
+ }).replace(/^#{1,6}\s+/gm, "").replace(/^(?:---|\*\*\*|___)\s*$/gm, "");
1796
+ result = stripInlineMarks(result).replace(/!\[([^\]]*)\]\([^)]+\)/g, "$1").replace(/\[([^\]]+)\]\([^)]+\)/g, "$1").replace(/^>\s?/gm, "").replace(/^\|[-:\s|]+\|\s*$/gm, "").replace(/^\|\s?/gm, "").replace(/\s?\|$/gm, "").replace(/\n{3,}/g, "\n\n").trim();
1797
+ result = result.replace(PLACEHOLDER_REGEX, (_match, index) => inlineCodeSnippets[Number(index)]);
1798
+ return result;
1799
+ }
1800
+
1801
+ // src/components/blocks/text-block/text-block.tsx
1802
+ var import_react_markdown = __toESM(require("react-markdown"), 1);
1803
+ var import_remark_breaks = __toESM(require("remark-breaks"), 1);
1804
+ var import_remark_gfm = __toESM(require("remark-gfm"), 1);
1805
+
1806
+ // src/components/blocks/scrollable-table/scrollable-table-styles.ts
1807
+ var import_styled_components3 = __toESM(require("styled-components"), 1);
1808
+ var TableContainer = import_styled_components3.default.div`
1809
+ position: relative;
1810
+ margin-block: 8px 16px;
1811
+ `;
1812
+ var TableScroll = import_styled_components3.default.div`
1813
+ width: 100%;
1814
+ overflow-x: auto;
1815
+ overflow-y: hidden;
1816
+ `;
1817
+
1818
+ // src/components/blocks/scrollable-table/scrollable-table.tsx
1819
+ var import_jsx_runtime21 = require("react/jsx-runtime");
1820
+ function ScrollableTable({ node: _node, ...props }) {
1821
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TableContainer, { children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TableScroll, { children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("table", { ...props }) }) });
1822
+ }
1823
+
1824
+ // src/components/blocks/text-block/sanitize.ts
1825
+ function sanitize(text) {
1826
+ return text.split("\n").filter((line) => {
1827
+ const t = line.trim();
1828
+ return !(t.startsWith("<!-- table-title:") && t.endsWith("-->"));
1829
+ }).join("\n");
1830
+ }
1831
+
1832
+ // src/components/blocks/text-block/text-block-styles.ts
1833
+ var import_styled_components4 = __toESM(require("styled-components"), 1);
1834
+ var TextBlockWrapper = import_styled_components4.default.div`
1835
+ ${typographyMixin(typographyTypes.GEIST_BODY_S_REGULAR)};
1836
+ color: ${colors["brown-100"]};
1837
+ word-break: break-word;
1838
+
1839
+ & h1 {
1840
+ ${typographyMixin(typographyTypes.GEIST_HEADING_S_BOLD)};
1841
+ margin-block: 20px;
1842
+ }
1843
+
1844
+ & h2 {
1845
+ ${typographyMixin(typographyTypes.GEIST_BODY_L_SEMI_BOLD)};
1846
+ margin-block: 12px;
1847
+ }
1848
+
1849
+ & h3,
1850
+ & h4,
1851
+ & h5,
1852
+ & h6 {
1853
+ ${typographyMixin(typographyTypes.GEIST_BODY_M_SEMI_BOLD)};
1854
+ margin-block: 8px;
1855
+ }
1856
+
1857
+ & strong {
1858
+ ${typographyMixin(typographyTypes.GEIST_BODY_S_SEMI_BOLD)};
1859
+ }
1860
+
1861
+ & p {
1862
+ margin-block: 0px;
1863
+ }
1864
+
1865
+ & p + p {
1866
+ margin-block: 8px 0px;
1867
+ }
1868
+
1869
+ & ul,
1870
+ & ol {
1871
+ margin-block: 16px;
1872
+ padding-left: 20px;
1873
+ }
1874
+
1875
+ & li + li {
1876
+ margin-block: 4px;
1877
+ }
1878
+
1879
+ & ol {
1880
+ list-style-type: decimal;
1881
+ }
1882
+
1883
+ & ol ol {
1884
+ list-style-type: lower-alpha;
1885
+ }
1886
+
1887
+ & ol ol ol {
1888
+ list-style-type: lower-roman;
1889
+ }
1890
+
1891
+ & a {
1892
+ color: inherit;
1893
+ text-decoration-line: underline;
1894
+ text-decoration-style: dotted;
1895
+ text-decoration-thickness: auto;
1896
+ text-underline-offset: auto;
1897
+ text-underline-position: from-font;
1898
+ transition: color 75ms ease-in-out;
1899
+
1900
+ &:hover {
1901
+ color: ${colors["blue-600"]};
1902
+ }
1903
+ }
1904
+
1905
+ & hr {
1906
+ border: none;
1907
+ border-top: 1px solid ${colors["brown-40"]};
1908
+ margin-block: 20px;
1909
+ }
1910
+
1911
+ & table {
1912
+ margin: 0;
1913
+ width: 100%;
1914
+ border-collapse: collapse;
1915
+ }
1916
+
1917
+ & th,
1918
+ & td {
1919
+ border: none;
1920
+ border-bottom: 1px solid ${colors["brown-40"]};
1921
+ text-align: left;
1922
+ }
1923
+
1924
+ & th {
1925
+ ${typographyMixin(typographyTypes.GEIST_LABEL_CAPTION_MEDIUM)};
1926
+ padding: 8px;
1927
+ }
1928
+
1929
+ & td {
1930
+ ${typographyMixin(typographyTypes.GEIST_BODY_XS_MEDIUM)};
1931
+ padding: 16px 8px;
1932
+ min-width: 150px;
1215
1933
  width: 1%;
1216
1934
  }
1217
1935
 
@@ -1231,12 +1949,12 @@ var TextBlockWrapper = import_styled_components4.default.div`
1231
1949
  `;
1232
1950
 
1233
1951
  // src/components/blocks/text-block/text-block.tsx
1234
- var import_jsx_runtime11 = require("react/jsx-runtime");
1952
+ var import_jsx_runtime22 = require("react/jsx-runtime");
1235
1953
  var markdownComponents = {
1236
- table: ((tableProps) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ScrollableTable, { ...tableProps }))
1954
+ table: ((tableProps) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ScrollableTable, { ...tableProps }))
1237
1955
  };
1238
1956
  function TextBlock({ block }) {
1239
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(TextBlockWrapper, { children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_react_markdown.default, { remarkPlugins: [import_remark_gfm.default, import_remark_breaks.default], components: markdownComponents, children: sanitize(block.payload.text) }) });
1957
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(TextBlockWrapper, { children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_react_markdown.default, { remarkPlugins: [import_remark_gfm.default, import_remark_breaks.default], components: markdownComponents, children: sanitize(block.payload.text) }) });
1240
1958
  }
1241
1959
 
1242
1960
  // src/components/blocks/block-services/text-block-service.ts
@@ -1258,12 +1976,12 @@ var BlockServices = {
1258
1976
  };
1259
1977
 
1260
1978
  // src/components/blocks/block/block.tsx
1261
- var import_jsx_runtime12 = require("react/jsx-runtime");
1979
+ var import_jsx_runtime23 = require("react/jsx-runtime");
1262
1980
  function Block({ block }) {
1263
1981
  const service = BlockServices[block.type];
1264
1982
  if (!service) return null;
1265
1983
  const { Component } = service;
1266
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Component, { block });
1984
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(Component, { block });
1267
1985
  }
1268
1986
 
1269
1987
  // src/components/chat/hooks/copy-message-text.ts
@@ -1281,13 +1999,13 @@ function copyMessageText(message) {
1281
1999
  }
1282
2000
 
1283
2001
  // src/components/chat/chat-message-actions/chat-message-actions.tsx
1284
- var import_react10 = require("react");
2002
+ var import_react14 = require("react");
1285
2003
 
1286
2004
  // src/components/chat/chat-context/chat-context.ts
1287
- var import_react8 = require("react");
1288
- var ChatContext = (0, import_react8.createContext)(null);
2005
+ var import_react12 = require("react");
2006
+ var ChatContext = (0, import_react12.createContext)(null);
1289
2007
  function useChatContext() {
1290
- const context = (0, import_react8.useContext)(ChatContext);
2008
+ const context = (0, import_react12.useContext)(ChatContext);
1291
2009
  if (!context) {
1292
2010
  throw new Error("useChatContext must be used within a Chat.Root component");
1293
2011
  }
@@ -1295,7 +2013,7 @@ function useChatContext() {
1295
2013
  }
1296
2014
 
1297
2015
  // src/components/chat/hooks/use-copy-to-clipboard.ts
1298
- var import_react9 = require("react");
2016
+ var import_react13 = require("react");
1299
2017
 
1300
2018
  // src/utils/clipboard.ts
1301
2019
  function copyToClipboard(text, options) {
@@ -1308,14 +2026,14 @@ function copyToClipboard(text, options) {
1308
2026
  // src/components/chat/hooks/use-copy-to-clipboard.ts
1309
2027
  var RESET_DELAY_MS = 2e3;
1310
2028
  function useCopyToClipboard() {
1311
- const [isCopied, setIsCopied] = (0, import_react9.useState)(false);
1312
- const timeoutRef = (0, import_react9.useRef)(null);
1313
- (0, import_react9.useEffect)(() => {
2029
+ const [isCopied, setIsCopied] = (0, import_react13.useState)(false);
2030
+ const timeoutRef = (0, import_react13.useRef)(null);
2031
+ (0, import_react13.useEffect)(() => {
1314
2032
  return () => {
1315
2033
  if (timeoutRef.current) clearTimeout(timeoutRef.current);
1316
2034
  };
1317
2035
  }, []);
1318
- const copy = (0, import_react9.useCallback)((text) => {
2036
+ const copy = (0, import_react13.useCallback)((text) => {
1319
2037
  copyToClipboard(text, {
1320
2038
  onSuccess: () => {
1321
2039
  setIsCopied(true);
@@ -1355,24 +2073,24 @@ var AnimatedAction = import_styled_components5.default.div`
1355
2073
  `;
1356
2074
 
1357
2075
  // src/components/chat/chat-message-actions/chat-message-actions.tsx
1358
- var import_jsx_runtime13 = require("react/jsx-runtime");
2076
+ var import_jsx_runtime24 = require("react/jsx-runtime");
1359
2077
  function ChatMessageActions({ className, messageId, content, role, isHelpful }) {
1360
2078
  const { onCopyMessage, onThumbUpClick, onThumbDownClick } = useChatContext();
1361
2079
  const { isCopied, copy } = useCopyToClipboard();
1362
- const handleCopy = (0, import_react10.useCallback)(() => {
2080
+ const handleCopy = (0, import_react14.useCallback)(() => {
1363
2081
  copy(content);
1364
2082
  onCopyMessage?.(messageId);
1365
2083
  }, [content, messageId, copy, onCopyMessage]);
1366
- const handleThumbUp = (0, import_react10.useCallback)(() => {
2084
+ const handleThumbUp = (0, import_react14.useCallback)(() => {
1367
2085
  onThumbUpClick?.(messageId, isHelpful === true ? null : true);
1368
2086
  }, [messageId, isHelpful, onThumbUpClick]);
1369
- const handleThumbDown = (0, import_react10.useCallback)(() => {
2087
+ const handleThumbDown = (0, import_react14.useCallback)(() => {
1370
2088
  onThumbDownClick?.(messageId, isHelpful === false ? null : false);
1371
2089
  }, [messageId, isHelpful, onThumbDownClick]);
1372
2090
  const isAssistant = role === MESSAGE_ROLE.ASSISTANT;
1373
2091
  const hasFeedback = isAssistant && (onThumbUpClick || onThumbDownClick);
1374
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(ActionsContainer, { $role: role, className, children: [
1375
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2092
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(ActionsContainer, { $role: role, className, children: [
2093
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
1376
2094
  IconButton,
1377
2095
  {
1378
2096
  icon: isCopied ? IconCheck : IconCopy,
@@ -1380,7 +2098,7 @@ function ChatMessageActions({ className, messageId, content, role, isHelpful })
1380
2098
  "aria-label": isCopied ? "Copied" : "Copy message"
1381
2099
  }
1382
2100
  ),
1383
- hasFeedback && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(AnimatedAction, { $visible: isHelpful !== false, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2101
+ hasFeedback && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(AnimatedAction, { $visible: isHelpful !== false, children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
1384
2102
  IconButton,
1385
2103
  {
1386
2104
  icon: IconThumbUp,
@@ -1389,7 +2107,7 @@ function ChatMessageActions({ className, messageId, content, role, isHelpful })
1389
2107
  "aria-label": "Good response"
1390
2108
  }
1391
2109
  ) }),
1392
- hasFeedback && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(AnimatedAction, { $visible: isHelpful !== true, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2110
+ hasFeedback && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(AnimatedAction, { $visible: isHelpful !== true, children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
1393
2111
  IconButton,
1394
2112
  {
1395
2113
  icon: IconThumbDown,
@@ -1439,12 +2157,12 @@ var MessageBubble = import_styled_components6.default.div`
1439
2157
  `;
1440
2158
 
1441
2159
  // src/components/chat/chat-message/chat-message.tsx
1442
- var import_jsx_runtime14 = require("react/jsx-runtime");
2160
+ var import_jsx_runtime25 = require("react/jsx-runtime");
1443
2161
  function ChatMessage({ message }) {
1444
- const clipboardText = (0, import_react11.useMemo)(() => copyMessageText(message), [message]);
1445
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(MessageRow, { $role: message.role, role: "article", "aria-label": `${message.role} message`, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(MessageContainer, { $role: message.role, children: [
1446
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(MessageBubble, { $role: message.role, "data-testid": "message-bubble", children: message.blocks.map((block, index) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Block, { block }, index)) }),
1447
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2162
+ const clipboardText = (0, import_react15.useMemo)(() => copyMessageText(message), [message]);
2163
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(MessageRow, { $role: message.role, role: "article", "aria-label": `${message.role} message`, children: /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(MessageContainer, { $role: message.role, children: [
2164
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(MessageBubble, { $role: message.role, "data-testid": "message-bubble", children: message.blocks.map((block, index) => /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(Block, { block }, index)) }),
2165
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
1448
2166
  ChatMessageActions,
1449
2167
  {
1450
2168
  messageId: message.id,