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