@shapesos/clay 0.10.0 → 0.11.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 (72) hide show
  1. package/dist/artifacts.cjs +741 -0
  2. package/dist/artifacts.cjs.map +1 -0
  3. package/dist/artifacts.d.cts +56 -0
  4. package/dist/artifacts.d.ts +56 -0
  5. package/dist/artifacts.js +23 -0
  6. package/dist/blocks.cjs +685 -53
  7. package/dist/blocks.cjs.map +1 -1
  8. package/dist/blocks.css +2 -0
  9. package/dist/blocks.d.cts +22 -10
  10. package/dist/blocks.d.ts +22 -10
  11. package/dist/blocks.js +15 -5
  12. package/dist/button.d.cts +2 -2
  13. package/dist/button.d.ts +2 -2
  14. package/dist/chat.cjs +728 -296
  15. package/dist/chat.cjs.map +1 -1
  16. package/dist/chat.d.cts +27 -10
  17. package/dist/chat.d.ts +27 -10
  18. package/dist/chat.js +7 -3
  19. package/dist/chunk-BX5TCEPR.js +436 -0
  20. package/dist/chunk-BX5TCEPR.js.map +1 -0
  21. package/dist/chunk-JGMN6W72.js +12 -0
  22. package/dist/chunk-JGMN6W72.js.map +1 -0
  23. package/dist/{chunk-27GJUWVN.js → chunk-JJUIBBBU.js} +14 -8
  24. package/dist/chunk-JJUIBBBU.js.map +1 -0
  25. package/dist/chunk-L35M3OD5.js +99 -0
  26. package/dist/chunk-L35M3OD5.js.map +1 -0
  27. package/dist/chunk-MEJESPTZ.js +1 -0
  28. package/dist/chunk-MEJESPTZ.js.map +1 -0
  29. package/dist/{chunk-R3BGPOAM.js → chunk-MXOPG747.js} +53 -45
  30. package/dist/chunk-MXOPG747.js.map +1 -0
  31. package/dist/{chunk-MLCRDVQ2.js → chunk-OBOXCBDL.js} +13 -5
  32. package/dist/chunk-OBOXCBDL.js.map +1 -0
  33. package/dist/{chunk-WS4IPADR.js → chunk-OUW6PUEB.js} +42 -110
  34. package/dist/chunk-OUW6PUEB.js.map +1 -0
  35. package/dist/icon.cjs +12 -4
  36. package/dist/icon.cjs.map +1 -1
  37. package/dist/icon.d.cts +19 -6
  38. package/dist/icon.d.ts +19 -6
  39. package/dist/icon.js +1 -1
  40. package/dist/index.cjs +794 -332
  41. package/dist/index.cjs.map +1 -1
  42. package/dist/index.d.cts +6 -4
  43. package/dist/index.d.ts +6 -4
  44. package/dist/index.js +50 -25
  45. package/dist/lottie.d.cts +2 -2
  46. package/dist/lottie.d.ts +2 -2
  47. package/dist/table.cjs +142 -0
  48. package/dist/table.cjs.map +1 -0
  49. package/dist/table.d.cts +44 -0
  50. package/dist/table.d.ts +44 -0
  51. package/dist/table.js +24 -0
  52. package/dist/table.js.map +1 -0
  53. package/dist/text-area.d.cts +2 -2
  54. package/dist/text-area.d.ts +2 -2
  55. package/dist/types-3Gzk7cRt.d.cts +121 -0
  56. package/dist/types-3Gzk7cRt.d.ts +121 -0
  57. package/dist/types-C9XX-Uhk.d.ts +68 -0
  58. package/dist/types-DuuRI4ll.d.cts +68 -0
  59. package/dist/utils.cjs +14 -7
  60. package/dist/utils.cjs.map +1 -1
  61. package/dist/utils.d.cts +18 -1
  62. package/dist/utils.d.ts +18 -1
  63. package/dist/utils.js +6 -4
  64. package/package.json +29 -4
  65. package/dist/chunk-27GJUWVN.js.map +0 -1
  66. package/dist/chunk-MLCRDVQ2.js.map +0 -1
  67. package/dist/chunk-OKPNST44.js +0 -1
  68. package/dist/chunk-R3BGPOAM.js.map +0 -1
  69. package/dist/chunk-WS4IPADR.js.map +0 -1
  70. package/dist/types-Q9aqd9nq.d.cts +0 -34
  71. package/dist/types-Q9aqd9nq.d.ts +0 -34
  72. /package/dist/{chunk-OKPNST44.js.map → artifacts.js.map} +0 -0
package/dist/index.cjs CHANGED
@@ -16342,6 +16342,10 @@ var require_lottie = __commonJS({
16342
16342
  // src/index.ts
16343
16343
  var src_exports = {};
16344
16344
  __export(src_exports, {
16345
+ ArtifactDownloadButton: () => ArtifactDownloadButton,
16346
+ ArtifactRefBlock: () => ArtifactRefBlock,
16347
+ ArtifactRefBlockService: () => ArtifactRefBlockService,
16348
+ ArtifactServices: () => ArtifactServices,
16345
16349
  BUTTON_INTENT: () => BUTTON_INTENT,
16346
16350
  BUTTON_SIZE: () => BUTTON_SIZE,
16347
16351
  BUTTON_VARIANT: () => BUTTON_VARIANT,
@@ -16359,12 +16363,18 @@ __export(src_exports, {
16359
16363
  KeyboardShortcut: () => KeyboardShortcut,
16360
16364
  Lottie: () => Lottie,
16361
16365
  MESSAGE_ROLE: () => MESSAGE_ROLE,
16366
+ TableArtifact: () => TableArtifact,
16362
16367
  TextArea: () => TextArea,
16363
16368
  TextBlockService: () => TextBlockService,
16369
+ artifactToClipboardText: () => artifactToClipboardText,
16370
+ artifactTypes: () => artifactTypes,
16364
16371
  blockTypes: () => blockTypes,
16365
16372
  colors: () => colors,
16366
16373
  convertTableToCSV: () => convertTableToCSV,
16374
+ copyMessageText: () => copyMessageText,
16375
+ escapeCsvField: () => escapeCsvField,
16367
16376
  fontFamilies: () => fontFamilies,
16377
+ isSafeDownloadUrl: () => isSafeDownloadUrl,
16368
16378
  markdownToPlainText: () => markdownToPlainText,
16369
16379
  mergeRefs: () => mergeRefs,
16370
16380
  parseMarkdownTable: () => parseMarkdownTable,
@@ -16760,55 +16770,678 @@ function typographyMixin(type) {
16760
16770
  ].join(";\n ");
16761
16771
  }
16762
16772
 
16773
+ // src/components/blocks/types.ts
16774
+ var blockTypes = {
16775
+ TEXT: "TEXT",
16776
+ ARTIFACT_REF: "ARTIFACT_REF"
16777
+ };
16778
+
16779
+ // src/components/artifacts/types.ts
16780
+ var artifactTypes = {
16781
+ TABLE: "TABLE"
16782
+ };
16783
+
16784
+ // src/components/artifacts/table-artifact/table-artifact.tsx
16785
+ var import_react7 = require("react");
16786
+
16787
+ // src/lib/utils.ts
16788
+ var import_clsx = require("clsx");
16789
+ var import_tailwind_merge = require("tailwind-merge");
16790
+ function cn(...inputs) {
16791
+ return (0, import_tailwind_merge.twMerge)((0, import_clsx.clsx)(inputs));
16792
+ }
16793
+
16794
+ // src/components/ai-elements/artifact.tsx
16795
+ var import_jsx_runtime = require("react/jsx-runtime");
16796
+ var Artifact = ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
16797
+ "div",
16798
+ {
16799
+ className: cn("flex flex-col overflow-hidden rounded-lg border bg-background shadow-sm", className),
16800
+ ...props
16801
+ }
16802
+ );
16803
+ var ArtifactHeader = ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: cn("flex items-center justify-between border-b bg-muted/50 px-4 py-3", className), ...props });
16804
+ var ArtifactTitle = ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: cn("font-medium text-foreground text-sm", className), ...props });
16805
+ var ArtifactActions = ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: cn("flex items-center gap-1", className), ...props });
16806
+ var ArtifactContent = ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: cn("flex-1 overflow-auto p-4", className), ...props });
16807
+
16808
+ // node_modules/@tabler/icons-react/dist/esm/createReactComponent.mjs
16809
+ var import_react = require("react");
16810
+
16811
+ // node_modules/@tabler/icons-react/dist/esm/defaultAttributes.mjs
16812
+ var defaultAttributes = {
16813
+ outline: {
16814
+ xmlns: "http://www.w3.org/2000/svg",
16815
+ width: 24,
16816
+ height: 24,
16817
+ viewBox: "0 0 24 24",
16818
+ fill: "none",
16819
+ stroke: "currentColor",
16820
+ strokeWidth: 2,
16821
+ strokeLinecap: "round",
16822
+ strokeLinejoin: "round"
16823
+ },
16824
+ filled: {
16825
+ xmlns: "http://www.w3.org/2000/svg",
16826
+ width: 24,
16827
+ height: 24,
16828
+ viewBox: "0 0 24 24",
16829
+ fill: "currentColor",
16830
+ stroke: "none"
16831
+ }
16832
+ };
16833
+
16834
+ // node_modules/@tabler/icons-react/dist/esm/createReactComponent.mjs
16835
+ var createReactComponent = (type, iconName, iconNamePascal, iconNode) => {
16836
+ const Component = (0, import_react.forwardRef)(
16837
+ ({ color = "currentColor", size = 24, stroke = 2, title, className, children, ...rest }, ref) => (0, import_react.createElement)(
16838
+ "svg",
16839
+ {
16840
+ ref,
16841
+ ...defaultAttributes[type],
16842
+ width: size,
16843
+ height: size,
16844
+ className: [`tabler-icon`, `tabler-icon-${iconName}`, className].join(" "),
16845
+ ...type === "filled" ? {
16846
+ fill: color
16847
+ } : {
16848
+ strokeWidth: stroke,
16849
+ stroke: color
16850
+ },
16851
+ ...rest
16852
+ },
16853
+ [
16854
+ title && (0, import_react.createElement)("title", { key: "svg-title" }, title),
16855
+ ...iconNode.map(([tag, attrs]) => (0, import_react.createElement)(tag, attrs)),
16856
+ ...Array.isArray(children) ? children : [children]
16857
+ ]
16858
+ )
16859
+ );
16860
+ Component.displayName = `${iconNamePascal}`;
16861
+ return Component;
16862
+ };
16863
+
16864
+ // node_modules/@tabler/icons-react/dist/esm/icons/IconCheck.mjs
16865
+ var __iconNode = [["path", { "d": "M5 12l5 5l10 -10", "key": "svg-0" }]];
16866
+ var IconCheck = createReactComponent("outline", "check", "Check", __iconNode);
16867
+
16868
+ // node_modules/@tabler/icons-react/dist/esm/icons/IconCopy.mjs
16869
+ var __iconNode2 = [["path", { "d": "M7 9.667a2.667 2.667 0 0 1 2.667 -2.667h8.666a2.667 2.667 0 0 1 2.667 2.667v8.666a2.667 2.667 0 0 1 -2.667 2.667h-8.666a2.667 2.667 0 0 1 -2.667 -2.667l0 -8.666", "key": "svg-0" }], ["path", { "d": "M4.012 16.737a2.005 2.005 0 0 1 -1.012 -1.737v-10c0 -1.1 .9 -2 2 -2h10c.75 0 1.158 .385 1.5 1", "key": "svg-1" }]];
16870
+ var IconCopy = createReactComponent("outline", "copy", "Copy", __iconNode2);
16871
+
16872
+ // node_modules/@tabler/icons-react/dist/esm/icons/IconDownload.mjs
16873
+ var __iconNode3 = [["path", { "d": "M4 17v2a2 2 0 0 0 2 2h12a2 2 0 0 0 2 -2v-2", "key": "svg-0" }], ["path", { "d": "M7 11l5 5l5 -5", "key": "svg-1" }], ["path", { "d": "M12 4l0 12", "key": "svg-2" }]];
16874
+ var IconDownload = createReactComponent("outline", "download", "Download", __iconNode3);
16875
+
16876
+ // node_modules/@tabler/icons-react/dist/esm/icons/IconThumbDown.mjs
16877
+ var __iconNode4 = [["path", { "d": "M7 13v-8a1 1 0 0 0 -1 -1h-2a1 1 0 0 0 -1 1v7a1 1 0 0 0 1 1h3a4 4 0 0 1 4 4v1a2 2 0 0 0 4 0v-5h3a2 2 0 0 0 2 -2l-1 -5a2 3 0 0 0 -2 -2h-7a3 3 0 0 0 -3 3", "key": "svg-0" }]];
16878
+ var IconThumbDown = createReactComponent("outline", "thumb-down", "ThumbDown", __iconNode4);
16879
+
16880
+ // node_modules/@tabler/icons-react/dist/esm/icons/IconThumbUp.mjs
16881
+ var __iconNode5 = [["path", { "d": "M7 11v8a1 1 0 0 1 -1 1h-2a1 1 0 0 1 -1 -1v-7a1 1 0 0 1 1 -1h3a4 4 0 0 0 4 -4v-1a2 2 0 0 1 4 0v5h3a2 2 0 0 1 2 2l-1 5a2 3 0 0 1 -2 2h-7a3 3 0 0 1 -3 -3", "key": "svg-0" }]];
16882
+ var IconThumbUp = createReactComponent("outline", "thumb-up", "ThumbUp", __iconNode5);
16883
+
16884
+ // src/components/icon-button/icon-button.tsx
16885
+ var import_react3 = require("react");
16886
+
16887
+ // src/components/icon/icon.tsx
16888
+ var import_react2 = require("react");
16889
+
16890
+ // src/components/icon/icon-styles.ts
16891
+ var import_styled_components = __toESM(require("styled-components"), 1);
16892
+ var STROKE_WIDTH_BY_SIZE = {
16893
+ 12: 1.4,
16894
+ 14: 1.4,
16895
+ 16: 1.8,
16896
+ 18: 1.8,
16897
+ 20: 1.8,
16898
+ 24: 1.8
16899
+ };
16900
+ var DEFAULT_STROKE_WIDTH = 1.8;
16901
+ function getStrokeWidth(size) {
16902
+ return STROKE_WIDTH_BY_SIZE[size] ?? DEFAULT_STROKE_WIDTH;
16903
+ }
16904
+ var IconWrapper = import_styled_components.default.span`
16905
+ display: inline-flex;
16906
+ align-items: center;
16907
+ justify-content: center;
16908
+ color: ${({ $color }) => $color ?? "currentColor"};
16909
+ flex-shrink: 0;
16910
+ `;
16911
+
16912
+ // src/components/icon/icon.tsx
16913
+ var import_jsx_runtime2 = require("react/jsx-runtime");
16914
+ var DEFAULT_SIZE = 16;
16915
+ var Icon = (0, import_react2.forwardRef)(function Icon2({ icon: IconComponent, size = DEFAULT_SIZE, color, className, "aria-label": ariaLabel }, ref) {
16916
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
16917
+ IconWrapper,
16918
+ {
16919
+ ref,
16920
+ className,
16921
+ $color: color,
16922
+ "aria-label": ariaLabel,
16923
+ role: ariaLabel ? "img" : void 0,
16924
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(IconComponent, { width: size, height: size, strokeWidth: getStrokeWidth(size) })
16925
+ }
16926
+ );
16927
+ });
16928
+
16929
+ // src/components/icon-button/icon-button-styles.ts
16930
+ var import_styled_components2 = __toESM(require("styled-components"), 1);
16931
+ var PADDING = {
16932
+ short: {
16933
+ small: "4px",
16934
+ medium: "6px"
16935
+ },
16936
+ long: {
16937
+ small: "4px 6px",
16938
+ medium: "6px 10px"
16939
+ }
16940
+ };
16941
+ var BORDER_RADIUS = {
16942
+ small: 6,
16943
+ medium: 10
16944
+ };
16945
+ var Button = import_styled_components2.default.button`
16946
+ display: inline-flex;
16947
+ align-items: center;
16948
+ justify-content: center;
16949
+ padding: ${({ $size, $variant }) => PADDING[$variant][$size]};
16950
+ border: none;
16951
+ border-radius: ${({ $size }) => BORDER_RADIUS[$size]}px;
16952
+ background: ${({ $isSelected }) => $isSelected ? colors["brown-40"] : "transparent"};
16953
+ color: ${colors["brown-100"]};
16954
+ cursor: pointer;
16955
+ transition: background-color 100ms ease;
16956
+
16957
+ &:hover {
16958
+ background: ${({ $isSelected }) => $isSelected ? colors["brown-40"] : colors["brown-20"]};
16959
+ }
16960
+
16961
+ &:active {
16962
+ background: ${colors["brown-40"]};
16963
+ }
16964
+
16965
+ &:disabled {
16966
+ color: ${colors["brown-50"]};
16967
+ background: transparent;
16968
+ cursor: not-allowed;
16969
+ }
16970
+ `;
16971
+
16972
+ // src/components/icon-button/icon-button.tsx
16973
+ var import_jsx_runtime3 = require("react/jsx-runtime");
16974
+ var ICON_SIZE_BY_BUTTON_SIZE = {
16975
+ small: 16,
16976
+ medium: 20
16977
+ };
16978
+ var IconButton = (0, import_react3.forwardRef)(function IconButton2({
16979
+ icon,
16980
+ size = "small",
16981
+ variant = "short",
16982
+ isSelected = false,
16983
+ disabled = false,
16984
+ onClick,
16985
+ className,
16986
+ id,
16987
+ as,
16988
+ "aria-label": ariaLabel,
16989
+ ...rest
16990
+ }, ref) {
16991
+ const isNativeButton = as === void 0 || as === "button";
16992
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
16993
+ Button,
16994
+ {
16995
+ ref,
16996
+ className,
16997
+ id,
16998
+ as,
16999
+ $size: size,
17000
+ $variant: variant,
17001
+ $isSelected: isSelected,
17002
+ disabled,
17003
+ onClick: disabled ? void 0 : onClick,
17004
+ "aria-label": ariaLabel,
17005
+ ...isNativeButton ? { type: "button" } : {},
17006
+ ...rest,
17007
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Icon, { icon, size: ICON_SIZE_BY_BUTTON_SIZE[size] })
17008
+ }
17009
+ );
17010
+ });
17011
+
17012
+ // src/components/ui/tooltip.tsx
17013
+ var React = __toESM(require("react"), 1);
17014
+ var TooltipPrimitive = __toESM(require("@radix-ui/react-tooltip"), 1);
17015
+ var import_jsx_runtime4 = require("react/jsx-runtime");
17016
+ var TooltipProvider = TooltipPrimitive.Provider;
17017
+ var Tooltip = TooltipPrimitive.Root;
17018
+ var TooltipTrigger = TooltipPrimitive.Trigger;
17019
+ var TooltipContent = React.forwardRef(({ className, sideOffset = 4, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
17020
+ TooltipPrimitive.Content,
17021
+ {
17022
+ ref,
17023
+ sideOffset,
17024
+ className: cn(
17025
+ "z-50 overflow-hidden rounded-md border bg-popover px-3 py-1.5 text-sm text-popover-foreground shadow-md animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-[--radix-tooltip-content-transform-origin]",
17026
+ className
17027
+ ),
17028
+ ...props
17029
+ }
17030
+ ));
17031
+ TooltipContent.displayName = TooltipPrimitive.Content.displayName;
17032
+
17033
+ // src/components/artifacts/artifact-download-button/artifact-download-button.tsx
17034
+ var import_jsx_runtime5 = require("react/jsx-runtime");
17035
+ function ArtifactDownloadButton({ href }) {
17036
+ if (!isSafeDownloadUrl(href)) return null;
17037
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(TooltipProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Tooltip, { children: [
17038
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
17039
+ IconButton,
17040
+ {
17041
+ as: "a",
17042
+ href,
17043
+ download: true,
17044
+ rel: "noopener noreferrer",
17045
+ icon: IconDownload,
17046
+ size: "small",
17047
+ "aria-label": "Download artifact"
17048
+ }
17049
+ ) }),
17050
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(TooltipContent, { children: "Download" })
17051
+ ] }) });
17052
+ }
17053
+ function isSafeDownloadUrl(url) {
17054
+ try {
17055
+ const parsed = new URL(url);
17056
+ return parsed.protocol === "https:" || parsed.protocol === "http:";
17057
+ } catch {
17058
+ return false;
17059
+ }
17060
+ }
17061
+
17062
+ // src/components/ui/table.tsx
17063
+ var React2 = __toESM(require("react"), 1);
17064
+ var import_jsx_runtime6 = require("react/jsx-runtime");
17065
+ 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 }) }));
17066
+ Table.displayName = "Table";
17067
+ var TableHeader = React2.forwardRef(
17068
+ ({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("thead", { ref, className: cn("[&_tr]:border-b", className), ...props })
17069
+ );
17070
+ TableHeader.displayName = "TableHeader";
17071
+ var TableBody = React2.forwardRef(
17072
+ ({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("tbody", { ref, className: cn("[&_tr:last-child]:border-0", className), ...props })
17073
+ );
17074
+ TableBody.displayName = "TableBody";
17075
+ var TableFooter = React2.forwardRef(
17076
+ ({ 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 })
17077
+ );
17078
+ TableFooter.displayName = "TableFooter";
17079
+ var TableRow = React2.forwardRef(
17080
+ ({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
17081
+ "tr",
17082
+ {
17083
+ ref,
17084
+ className: cn("border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted", className),
17085
+ ...props
17086
+ }
17087
+ )
17088
+ );
17089
+ TableRow.displayName = "TableRow";
17090
+ var TableHead = React2.forwardRef(
17091
+ ({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
17092
+ "th",
17093
+ {
17094
+ ref,
17095
+ className: cn(
17096
+ "h-12 px-4 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0",
17097
+ className
17098
+ ),
17099
+ ...props
17100
+ }
17101
+ )
17102
+ );
17103
+ TableHead.displayName = "TableHead";
17104
+ var TableCell = React2.forwardRef(
17105
+ ({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("td", { ref, className: cn("p-4 align-middle [&:has([role=checkbox])]:pr-0", className), ...props })
17106
+ );
17107
+ TableCell.displayName = "TableCell";
17108
+ var TableCaption = React2.forwardRef(
17109
+ ({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("caption", { ref, className: cn("mt-4 text-sm text-muted-foreground", className), ...props })
17110
+ );
17111
+ TableCaption.displayName = "TableCaption";
17112
+
17113
+ // src/components/table/use-scroll-shadow.ts
17114
+ var import_react4 = require("react");
17115
+ function useScrollShadow() {
17116
+ const targetRef = (0, import_react4.useRef)(null);
17117
+ const [isScrolled, setIsScrolled] = (0, import_react4.useState)(false);
17118
+ (0, import_react4.useEffect)(() => {
17119
+ const target = targetRef.current;
17120
+ if (!target) return;
17121
+ let scrollContainer = target;
17122
+ while (scrollContainer) {
17123
+ const overflowY = getComputedStyle(scrollContainer).overflowY;
17124
+ if (overflowY === "auto" || overflowY === "scroll") break;
17125
+ scrollContainer = scrollContainer.parentElement;
17126
+ }
17127
+ if (!scrollContainer) return;
17128
+ setIsScrolled(scrollContainer.scrollTop > 0);
17129
+ const handleScroll = () => {
17130
+ if (!scrollContainer) return;
17131
+ setIsScrolled(scrollContainer.scrollTop > 0);
17132
+ };
17133
+ scrollContainer.addEventListener("scroll", handleScroll, { passive: true });
17134
+ return () => scrollContainer.removeEventListener("scroll", handleScroll);
17135
+ }, []);
17136
+ return { targetRef, isScrolled };
17137
+ }
17138
+
17139
+ // src/components/artifacts/table-artifact/table-artifact-styles.ts
17140
+ var import_react5 = require("react");
17141
+ function StatusMessage({ $tone, children }) {
17142
+ return (0, import_react5.createElement)(
17143
+ "div",
17144
+ {
17145
+ className: `py-6 px-2 text-center text-sm font-medium leading-5 ${$tone === "error" ? "text-red-600" : "text-brown-70"}`
17146
+ },
17147
+ children
17148
+ );
17149
+ }
17150
+
17151
+ // src/components/artifacts/table-artifact/table-artifact-content.constants.ts
17152
+ var TABLE_STATUS = {
17153
+ LOADING: "loading",
17154
+ READY: "ready",
17155
+ ERROR: "error",
17156
+ UNAVAILABLE: "unavailable"
17157
+ };
17158
+
17159
+ // src/components/artifacts/table-artifact/table-artifact-content.utils.ts
17160
+ function formatCell(row, column) {
17161
+ return row[column.key] ?? "";
17162
+ }
17163
+ var HEAD_CLASS = "h-auto py-2 px-4 text-xs font-medium text-foreground whitespace-nowrap text-left";
17164
+ var CELL_CLASS = "p-4 text-sm font-normal text-foreground align-top min-w-[150px] text-left";
17165
+ var ROW_CLASSES = "border-b-brown-30 bg-background/70 hover:bg-secondary";
17166
+ var DATA_ROW_CLASSES = `${ROW_CLASSES} animate-in fade-in duration-300`;
17167
+ var SCROLL_SHADOW = "0 1px 2px rgb(0 0 0 / 0.01), 0 6px 12px -2px rgb(0 0 0 / 0.03)";
17168
+
17169
+ // src/components/artifacts/table-artifact/table-artifact-content.tsx
17170
+ var import_jsx_runtime7 = require("react/jsx-runtime");
17171
+ function TableArtifactContent({
17172
+ columns,
17173
+ data: data2,
17174
+ knownRowCount,
17175
+ maxShimmerRows,
17176
+ maxBodyHeight,
17177
+ unavailableMessage
17178
+ }) {
17179
+ if (data2.status === TABLE_STATUS.ERROR) {
17180
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(StatusMessage, { $tone: "error", children: "Couldn't load table data." });
17181
+ }
17182
+ if (data2.status === TABLE_STATUS.UNAVAILABLE) {
17183
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(StatusMessage, { $tone: "info", children: unavailableMessage });
17184
+ }
17185
+ if (data2.status === TABLE_STATUS.LOADING) {
17186
+ if (typeof knownRowCount === "number" && knownRowCount > 0) {
17187
+ const shimmerCount = Math.min(knownRowCount, maxShimmerRows);
17188
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ArtifactTable, { columns, maxBodyHeight, children: renderShimmerRows(columns, shimmerCount) });
17189
+ }
17190
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(StatusMessage, { $tone: "info", children: "Loading table\u2026" });
17191
+ }
17192
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ArtifactTable, { columns, maxBodyHeight, children: data2.rows.length === 0 ? renderEmptyRow(columns.length) : renderDataRows(columns, data2.rows) });
17193
+ }
17194
+ function ArtifactTable({ columns, maxBodyHeight, children }) {
17195
+ const { targetRef, isScrolled } = useScrollShadow();
17196
+ 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: [
17197
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
17198
+ TableHeader,
17199
+ {
17200
+ "data-scrolled": isScrolled ? "true" : void 0,
17201
+ className: "sticky top-0 z-10 bg-background/70 backdrop-blur-lg transition-shadow",
17202
+ style: isScrolled ? { boxShadow: SCROLL_SHADOW } : void 0,
17203
+ 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)) })
17204
+ }
17205
+ ),
17206
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(TableBody, { children })
17207
+ ] }) });
17208
+ }
17209
+ function renderShimmerRows(columns, count) {
17210
+ return Array.from({ length: count }).map((_, index2) => /* @__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-${index2}`));
17211
+ }
17212
+ function renderDataRows(columns, rows) {
17213
+ return rows.map((row, index2) => /* @__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)) }, index2));
17214
+ }
17215
+ function renderEmptyRow(colSpan) {
17216
+ 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." }) });
17217
+ }
17218
+
17219
+ // src/components/artifacts/table-artifact/use-table-artifact-data.ts
17220
+ var import_react6 = require("react");
17221
+
17222
+ // src/components/artifacts/table-artifact/parse-csv.ts
17223
+ var import_papaparse = __toESM(require("papaparse"), 1);
17224
+ function parseCsv(text2, columns) {
17225
+ const result = import_papaparse.default.parse(text2, {
17226
+ header: true,
17227
+ skipEmptyLines: true,
17228
+ dynamicTyping: false
17229
+ });
17230
+ const csvHeaders = new Set(result.meta?.fields ?? []);
17231
+ const presentColumns = columns.filter((col) => csvHeaders.has(col.key));
17232
+ return result.data.map((rawRow) => {
17233
+ const row = {};
17234
+ for (const column of presentColumns) {
17235
+ const value2 = rawRow[column.key];
17236
+ row[column.key] = value2 === void 0 || value2 === "" ? null : value2;
17237
+ }
17238
+ return row;
17239
+ });
17240
+ }
17241
+
17242
+ // src/components/artifacts/table-artifact/use-table-artifact-data.ts
17243
+ var LOADING = { status: "loading", rows: null, error: null };
17244
+ var UNAVAILABLE = { status: "unavailable", rows: null, error: null };
17245
+ var INITIAL_INTERNAL = { cacheKey: null, data: LOADING };
17246
+ function columnsFingerprint(columns) {
17247
+ return columns.map((c) => `${c.key}:${c.type ?? "string"}`).join("|");
17248
+ }
17249
+ function useTableArtifactData(presignedUrl, columns) {
17250
+ const colsKey = (0, import_react6.useMemo)(() => columnsFingerprint(columns), [columns]);
17251
+ const cacheKey = presignedUrl ? `${presignedUrl}::${colsKey}` : null;
17252
+ const [state, setState] = (0, import_react6.useState)(INITIAL_INTERNAL);
17253
+ (0, import_react6.useEffect)(() => {
17254
+ if (!presignedUrl) return;
17255
+ const controller = new AbortController();
17256
+ fetch(presignedUrl, {
17257
+ signal: controller.signal,
17258
+ headers: { Accept: "text/csv" },
17259
+ // Presigned URLs are cross-origin S3 in production and don't need credentials. Explicit
17260
+ // omit so a same-origin URL (compromised payload, local dev) can't accidentally receive
17261
+ // session cookies or auth headers.
17262
+ credentials: "omit"
17263
+ }).then(async (response) => {
17264
+ if (!response.ok) throw new Error(`Failed to load table data (${response.status})`);
17265
+ return response.text();
17266
+ }).then((text2) => {
17267
+ if (controller.signal.aborted) return;
17268
+ try {
17269
+ const rows = parseCsv(text2, columns);
17270
+ setState({ cacheKey: `${presignedUrl}::${colsKey}`, data: { status: "ready", rows, error: null } });
17271
+ } catch (parseError) {
17272
+ console.error("[TableArtifact] CSV parse failed", parseError);
17273
+ const err = parseError instanceof Error ? parseError : new Error(String(parseError));
17274
+ setState({ cacheKey: `${presignedUrl}::${colsKey}`, data: { status: "error", rows: null, error: err } });
17275
+ }
17276
+ }).catch((error) => {
17277
+ if (controller.signal.aborted) return;
17278
+ console.error("[TableArtifact] Fetch failed", error);
17279
+ const err = error instanceof Error ? error : new Error(String(error));
17280
+ setState({ cacheKey: `${presignedUrl}::${colsKey}`, data: { status: "error", rows: null, error: err } });
17281
+ });
17282
+ return () => {
17283
+ controller.abort();
17284
+ };
17285
+ }, [presignedUrl, colsKey]);
17286
+ if (!presignedUrl) return UNAVAILABLE;
17287
+ if (state.cacheKey !== cacheKey) return LOADING;
17288
+ return state.data;
17289
+ }
17290
+
17291
+ // src/components/artifacts/table-artifact/validate.ts
17292
+ function isValidTableConfig(config) {
17293
+ if (!config || typeof config !== "object") return false;
17294
+ const candidate = config;
17295
+ if (!Array.isArray(candidate.columns)) return false;
17296
+ if (candidate.columns.length === 0) return false;
17297
+ return candidate.columns.every(isValidTableColumn);
17298
+ }
17299
+ function isValidTableColumn(column) {
17300
+ if (!column || typeof column !== "object") return false;
17301
+ const candidate = column;
17302
+ if (typeof candidate.key !== "string" || candidate.key.length === 0) return false;
17303
+ if (typeof candidate.label !== "string") return false;
17304
+ return true;
17305
+ }
17306
+
17307
+ // src/components/artifacts/table-artifact/table-artifact.tsx
17308
+ var import_jsx_runtime8 = require("react/jsx-runtime");
17309
+ var TITLE_CLASSES = "text-sm font-semibold text-foreground";
17310
+ var TABLE_MAX_BODY_HEIGHT = 480;
17311
+ var MAX_SHIMMER_ROWS = 10;
17312
+ var MSG_UNAVAILABLE = "Table data is unavailable.";
17313
+ function TableArtifact({ artifact }) {
17314
+ const validConfig = isValidTableConfig(artifact.config);
17315
+ const validColumns = validConfig ? artifact.config.columns : null;
17316
+ (0, import_react7.useEffect)(() => {
17317
+ if (validConfig) return;
17318
+ console.warn("[clay] TableArtifact: malformed `config` \u2014 skipping render.");
17319
+ }, [validConfig]);
17320
+ const data2 = useTableArtifactData(
17321
+ validConfig ? artifact.protectedAsset?.presignedUrl ?? null : null,
17322
+ validColumns ?? []
17323
+ );
17324
+ const presignedUrl = artifact.protectedAsset?.presignedUrl ?? null;
17325
+ if (!validConfig || !validColumns) {
17326
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Artifact, { className: "shadow-none w-full rounded-2xl bg-background/70", children: [
17327
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(ArtifactHeader, { className: "bg-background/70", children: [
17328
+ artifact.title ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ArtifactTitle, { className: TITLE_CLASSES, children: artifact.title }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", {}),
17329
+ presignedUrl ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ArtifactActions, { children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ArtifactDownloadButton, { href: presignedUrl }) }) : null
17330
+ ] }),
17331
+ /* @__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 }) })
17332
+ ] });
17333
+ }
17334
+ return (
17335
+ // The table body owns its own scroll container (`ArtifactTable` inside `TableArtifactContent`).
17336
+ // Disable the outer overflow container so only one scroll context exists — this prevents
17337
+ // double-scroll nesting and lets `useScrollShadow` find the correct scroll ancestor.
17338
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Artifact, { className: "shadow-none w-full rounded-2xl bg-background/70", children: [
17339
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(ArtifactHeader, { className: "bg-background/70", children: [
17340
+ artifact.title ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ArtifactTitle, { className: TITLE_CLASSES, children: artifact.title }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", {}),
17341
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ArtifactActions, { children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ArtifactDownloadButton, { href: presignedUrl ?? "" }) })
17342
+ ] }),
17343
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ArtifactContent, { className: "p-0 overflow-visible", style: { maxHeight: "none" }, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
17344
+ TableArtifactContent,
17345
+ {
17346
+ columns: validColumns,
17347
+ data: data2,
17348
+ knownRowCount: artifact.config.stats?.rowCount,
17349
+ maxShimmerRows: MAX_SHIMMER_ROWS,
17350
+ maxBodyHeight: TABLE_MAX_BODY_HEIGHT,
17351
+ unavailableMessage: MSG_UNAVAILABLE
17352
+ }
17353
+ ) })
17354
+ ] })
17355
+ );
17356
+ }
17357
+
17358
+ // src/components/artifacts/table-artifact/to-clipboard-text.ts
17359
+ function tableArtifactToClipboardText(artifact) {
17360
+ const trimmedTitle = artifact.title?.trim();
17361
+ return trimmedTitle || "Table";
17362
+ }
17363
+
17364
+ // src/components/artifacts/artifact-services/table-artifact-service.ts
17365
+ var TableArtifactService = {
17366
+ type: artifactTypes.TABLE,
17367
+ Component: TableArtifact,
17368
+ toClipboardText: tableArtifactToClipboardText
17369
+ };
17370
+
17371
+ // src/components/artifacts/artifact-services/index.ts
17372
+ var ArtifactServices = {
17373
+ [artifactTypes.TABLE]: TableArtifactService
17374
+ };
17375
+ function artifactToClipboardText(artifact) {
17376
+ const entry = ArtifactServices[artifact.type];
17377
+ if (!entry) {
17378
+ return `[Artifact: ${artifact.title ?? artifact.type}]`;
17379
+ }
17380
+ return entry.toClipboardText(artifact);
17381
+ }
17382
+
17383
+ // src/components/blocks/artifact-ref-block/artifact-ref-block.tsx
17384
+ var import_jsx_runtime9 = require("react/jsx-runtime");
17385
+ function ArtifactRefBlock({ block }) {
17386
+ const { artifact } = block.payload;
17387
+ const entry = ArtifactServices[artifact.type];
17388
+ if (!entry) return null;
17389
+ const { Component } = entry;
17390
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Component, { artifact });
17391
+ }
17392
+
17393
+ // src/components/blocks/block-services/artifact-ref-block-service.ts
17394
+ var ArtifactRefBlockService = {
17395
+ type: blockTypes.ARTIFACT_REF,
17396
+ Component: ArtifactRefBlock,
17397
+ /**
17398
+ * Delegates to the registered artifact component's `toClipboardText`. Falls back to
17399
+ * `[Artifact: <title>]` via `artifactToClipboardText`'s own fallback when the artifact type
17400
+ * has no registered component, so copy never silently produces an empty string.
17401
+ */
17402
+ toClipboardText: (block) => artifactToClipboardText(block.payload.artifact)
17403
+ };
17404
+
17405
+ // src/utils/markdown-to-plain-text.ts
17406
+ var PLACEHOLDER_PREFIX = "\u200B\u200BIC";
17407
+ var PLACEHOLDER_SUFFIX = "\u200B\u200B";
17408
+ var PLACEHOLDER_REGEX = new RegExp(`${PLACEHOLDER_PREFIX}(\\d+)${PLACEHOLDER_SUFFIX}`, "g");
17409
+ function stripInlineMarks(text2) {
17410
+ return text2.replace(/\*{3}(.+?)\*{3}/g, "$1").replace(/\*{2}(.+?)\*{2}/g, "$1").replace(/_{2}(.+?)_{2}/g, "$1").replace(/\*(.+?)\*/g, "$1").replace(/_(.+?)_/g, "$1").replace(/~~(.+?)~~/g, "$1");
17411
+ }
17412
+ function markdownToPlainText(markdown) {
17413
+ if (!markdown) return "";
17414
+ const inlineCodeSnippets = [];
17415
+ let result = markdown.replace(/^<!--[\s\S]*?--!?>\n?/gm, "").replace(/```[\s\S]*?```/g, "").replace(/`([^`]+)`/g, (_match, code) => {
17416
+ inlineCodeSnippets.push(code);
17417
+ return `${PLACEHOLDER_PREFIX}${inlineCodeSnippets.length - 1}${PLACEHOLDER_SUFFIX}`;
17418
+ }).replace(/^#{1,6}\s+/gm, "").replace(/^(?:---|\*\*\*|___)\s*$/gm, "");
17419
+ 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();
17420
+ result = result.replace(PLACEHOLDER_REGEX, (_match, index2) => inlineCodeSnippets[Number(index2)]);
17421
+ return result;
17422
+ }
17423
+
16763
17424
  // src/components/blocks/text-block/text-block.tsx
16764
17425
  var import_react_markdown = __toESM(require("react-markdown"), 1);
16765
17426
  var import_remark_breaks = __toESM(require("remark-breaks"), 1);
16766
17427
  var import_remark_gfm = __toESM(require("remark-gfm"), 1);
16767
17428
 
16768
- // src/components/blocks/block-context/block-context.tsx
16769
- var import_react = require("react");
16770
- var BlockContext = (0, import_react.createContext)(null);
16771
- function useBlockContext() {
16772
- return (0, import_react.useContext)(BlockContext) ?? {};
16773
- }
16774
-
16775
17429
  // src/components/blocks/scrollable-table/scrollable-table-styles.ts
16776
- var import_styled_components = __toESM(require("styled-components"), 1);
16777
- var TableContainer = import_styled_components.default.div`
17430
+ var import_styled_components3 = __toESM(require("styled-components"), 1);
17431
+ var TableContainer = import_styled_components3.default.div`
16778
17432
  position: relative;
16779
17433
  margin-block: 8px 16px;
16780
17434
  `;
16781
- var TableScroll = import_styled_components.default.div`
17435
+ var TableScroll = import_styled_components3.default.div`
16782
17436
  width: 100%;
16783
17437
  overflow-x: auto;
16784
17438
  overflow-y: hidden;
16785
17439
  `;
16786
- var TableActionsWrapper = import_styled_components.default.div`
16787
- display: flex;
16788
- justify-content: flex-start;
16789
- margin-block: 8px 0;
16790
- `;
16791
- var TableActionsDivider = import_styled_components.default.div`
16792
- height: 1px;
16793
- background: ${colors["brown-40"]};
16794
- margin-block: 8px 0;
16795
- `;
16796
17440
 
16797
17441
  // src/components/blocks/scrollable-table/scrollable-table.tsx
16798
- var import_jsx_runtime = require("react/jsx-runtime");
16799
- function ScrollableTable({ node: _node, counter, ...props }) {
16800
- const currentIndex = counter ? counter.value : 0;
16801
- if (counter) {
16802
- counter.value += 1;
16803
- }
16804
- const { TableActions } = useBlockContext();
16805
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(TableContainer, { children: [
16806
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableScroll, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("table", { ...props }) }),
16807
- TableActions && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
16808
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableActionsWrapper, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableActions, { tableIndex: currentIndex }) }),
16809
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableActionsDivider, {})
16810
- ] })
16811
- ] });
17442
+ var import_jsx_runtime10 = require("react/jsx-runtime");
17443
+ function ScrollableTable({ node: _node, ...props }) {
17444
+ 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 }) }) });
16812
17445
  }
16813
17446
 
16814
17447
  // src/components/blocks/text-block/sanitize.ts
@@ -16820,8 +17453,8 @@ function sanitize(text2) {
16820
17453
  }
16821
17454
 
16822
17455
  // src/components/blocks/text-block/text-block-styles.ts
16823
- var import_styled_components2 = __toESM(require("styled-components"), 1);
16824
- var TextBlockWrapper = import_styled_components2.default.div`
17456
+ var import_styled_components4 = __toESM(require("styled-components"), 1);
17457
+ var TextBlockWrapper = import_styled_components4.default.div`
16825
17458
  ${typographyMixin(typographyTypes.GEIST_BODY_S_REGULAR)};
16826
17459
  color: ${colors["brown-100"]};
16827
17460
  word-break: break-word;
@@ -16939,251 +17572,81 @@ var TextBlockWrapper = import_styled_components2.default.div`
16939
17572
  `;
16940
17573
 
16941
17574
  // src/components/blocks/text-block/text-block.tsx
16942
- var import_jsx_runtime2 = require("react/jsx-runtime");
17575
+ var import_jsx_runtime11 = require("react/jsx-runtime");
17576
+ var markdownComponents = {
17577
+ table: ((tableProps) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ScrollableTable, { ...tableProps }))
17578
+ };
16943
17579
  function TextBlock({ block }) {
16944
- const counter = { value: 0 };
16945
- const markdownComponents = {
16946
- table: ((tableProps) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ScrollableTable, { ...tableProps, counter }))
16947
- };
16948
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(TextBlockWrapper, { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_markdown.default, { remarkPlugins: [import_remark_gfm.default, import_remark_breaks.default], components: markdownComponents, children: sanitize(block.payload.text) }) });
17580
+ 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) }) });
16949
17581
  }
16950
17582
 
16951
- // src/components/blocks/types.ts
16952
- var blockTypes = {
16953
- TEXT: "TEXT"
16954
- };
16955
-
16956
17583
  // src/components/blocks/block-services/text-block-service.ts
16957
17584
  var TextBlockService = {
16958
17585
  type: blockTypes.TEXT,
16959
- Component: TextBlock
17586
+ Component: TextBlock,
17587
+ /**
17588
+ * Converts the block's markdown source to plain text so the final clipboard value is clean
17589
+ * when pasted into tools that don't render markdown. Each block type owns its own clipboard
17590
+ * conversion so `useCopyToClipboard` can write text verbatim without a second pass.
17591
+ */
17592
+ toClipboardText: (block) => markdownToPlainText(block.payload.text)
16960
17593
  };
16961
17594
 
16962
17595
  // src/components/blocks/block-services/index.ts
16963
17596
  var BlockServices = {
16964
- [TextBlockService.type]: TextBlockService
17597
+ [blockTypes.TEXT]: TextBlockService,
17598
+ [blockTypes.ARTIFACT_REF]: ArtifactRefBlockService
16965
17599
  };
16966
17600
 
16967
17601
  // src/components/blocks/block/block.tsx
16968
- var import_jsx_runtime3 = require("react/jsx-runtime");
17602
+ var import_jsx_runtime12 = require("react/jsx-runtime");
16969
17603
  function Block({ block }) {
16970
17604
  const service = BlockServices[block.type];
16971
17605
  if (!service) return null;
16972
17606
  const { Component } = service;
16973
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Component, { block });
17607
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Component, { block });
16974
17608
  }
16975
17609
 
16976
- // src/components/chat/chat-message/chat-message.tsx
16977
- var import_react7 = require("react");
16978
-
16979
- // src/components/chat/chat-context/chat-context.ts
16980
- var import_react2 = require("react");
16981
- var ChatContext = (0, import_react2.createContext)(null);
16982
- function useChatContext() {
16983
- const context = (0, import_react2.useContext)(ChatContext);
16984
- if (!context) {
16985
- throw new Error("useChatContext must be used within a Chat.Root component");
16986
- }
16987
- return context;
17610
+ // src/components/blocks/block-context/block-context.tsx
17611
+ var import_react8 = require("react");
17612
+ var BlockContext = (0, import_react8.createContext)(null);
17613
+ function useBlockContext() {
17614
+ return (0, import_react8.useContext)(BlockContext) ?? {};
16988
17615
  }
16989
17616
 
16990
- // src/components/chat/chat-message-actions/chat-message-actions.tsx
16991
- var import_react6 = require("react");
16992
-
16993
- // node_modules/@tabler/icons-react/dist/esm/createReactComponent.mjs
16994
- var import_react3 = require("react");
17617
+ // src/components/chat/chat-message/chat-message.tsx
17618
+ var import_react12 = require("react");
16995
17619
 
16996
- // node_modules/@tabler/icons-react/dist/esm/defaultAttributes.mjs
16997
- var defaultAttributes = {
16998
- outline: {
16999
- xmlns: "http://www.w3.org/2000/svg",
17000
- width: 24,
17001
- height: 24,
17002
- viewBox: "0 0 24 24",
17003
- fill: "none",
17004
- stroke: "currentColor",
17005
- strokeWidth: 2,
17006
- strokeLinecap: "round",
17007
- strokeLinejoin: "round"
17008
- },
17009
- filled: {
17010
- xmlns: "http://www.w3.org/2000/svg",
17011
- width: 24,
17012
- height: 24,
17013
- viewBox: "0 0 24 24",
17014
- fill: "currentColor",
17015
- stroke: "none"
17620
+ // src/components/chat/hooks/copy-message-text.ts
17621
+ function copyMessageText(message) {
17622
+ const parts = [];
17623
+ for (const block of message.blocks) {
17624
+ const service = BlockServices[block.type];
17625
+ if (!service) continue;
17626
+ if (!service.toClipboardText) continue;
17627
+ const text2 = service.toClipboardText(block);
17628
+ if (text2) parts.push(text2);
17016
17629
  }
17017
- };
17018
-
17019
- // node_modules/@tabler/icons-react/dist/esm/createReactComponent.mjs
17020
- var createReactComponent = (type, iconName, iconNamePascal, iconNode) => {
17021
- const Component = (0, import_react3.forwardRef)(
17022
- ({ color = "currentColor", size = 24, stroke = 2, title, className, children, ...rest }, ref) => (0, import_react3.createElement)(
17023
- "svg",
17024
- {
17025
- ref,
17026
- ...defaultAttributes[type],
17027
- width: size,
17028
- height: size,
17029
- className: [`tabler-icon`, `tabler-icon-${iconName}`, className].join(" "),
17030
- ...type === "filled" ? {
17031
- fill: color
17032
- } : {
17033
- strokeWidth: stroke,
17034
- stroke: color
17035
- },
17036
- ...rest
17037
- },
17038
- [
17039
- title && (0, import_react3.createElement)("title", { key: "svg-title" }, title),
17040
- ...iconNode.map(([tag, attrs]) => (0, import_react3.createElement)(tag, attrs)),
17041
- ...Array.isArray(children) ? children : [children]
17042
- ]
17043
- )
17044
- );
17045
- Component.displayName = `${iconNamePascal}`;
17046
- return Component;
17047
- };
17048
-
17049
- // node_modules/@tabler/icons-react/dist/esm/icons/IconCheck.mjs
17050
- var __iconNode = [["path", { "d": "M5 12l5 5l10 -10", "key": "svg-0" }]];
17051
- var IconCheck = createReactComponent("outline", "check", "Check", __iconNode);
17052
-
17053
- // node_modules/@tabler/icons-react/dist/esm/icons/IconCopy.mjs
17054
- var __iconNode2 = [["path", { "d": "M7 9.667a2.667 2.667 0 0 1 2.667 -2.667h8.666a2.667 2.667 0 0 1 2.667 2.667v8.666a2.667 2.667 0 0 1 -2.667 2.667h-8.666a2.667 2.667 0 0 1 -2.667 -2.667l0 -8.666", "key": "svg-0" }], ["path", { "d": "M4.012 16.737a2.005 2.005 0 0 1 -1.012 -1.737v-10c0 -1.1 .9 -2 2 -2h10c.75 0 1.158 .385 1.5 1", "key": "svg-1" }]];
17055
- var IconCopy = createReactComponent("outline", "copy", "Copy", __iconNode2);
17056
-
17057
- // node_modules/@tabler/icons-react/dist/esm/icons/IconThumbDown.mjs
17058
- var __iconNode3 = [["path", { "d": "M7 13v-8a1 1 0 0 0 -1 -1h-2a1 1 0 0 0 -1 1v7a1 1 0 0 0 1 1h3a4 4 0 0 1 4 4v1a2 2 0 0 0 4 0v-5h3a2 2 0 0 0 2 -2l-1 -5a2 3 0 0 0 -2 -2h-7a3 3 0 0 0 -3 3", "key": "svg-0" }]];
17059
- var IconThumbDown = createReactComponent("outline", "thumb-down", "ThumbDown", __iconNode3);
17060
-
17061
- // node_modules/@tabler/icons-react/dist/esm/icons/IconThumbUp.mjs
17062
- var __iconNode4 = [["path", { "d": "M7 11v8a1 1 0 0 1 -1 1h-2a1 1 0 0 1 -1 -1v-7a1 1 0 0 1 1 -1h3a4 4 0 0 0 4 -4v-1a2 2 0 0 1 4 0v5h3a2 2 0 0 1 2 2l-1 5a2 3 0 0 1 -2 2h-7a3 3 0 0 1 -3 -3", "key": "svg-0" }]];
17063
- var IconThumbUp = createReactComponent("outline", "thumb-up", "ThumbUp", __iconNode4);
17064
-
17065
- // src/components/icon/icon.tsx
17066
- var import_react4 = require("react");
17067
-
17068
- // src/components/icon/icon-styles.ts
17069
- var import_styled_components3 = __toESM(require("styled-components"), 1);
17070
- var STROKE_WIDTH_BY_SIZE = {
17071
- 12: 1.4,
17072
- 14: 1.4,
17073
- 16: 1.8,
17074
- 18: 1.8,
17075
- 20: 1.8,
17076
- 24: 1.8
17077
- };
17078
- var DEFAULT_STROKE_WIDTH = 1.8;
17079
- function getStrokeWidth(size) {
17080
- return STROKE_WIDTH_BY_SIZE[size] ?? DEFAULT_STROKE_WIDTH;
17630
+ if (parts.length === 0) return message.fallbackText;
17631
+ return parts.join("\n\n");
17081
17632
  }
17082
- var IconWrapper = import_styled_components3.default.span`
17083
- display: inline-flex;
17084
- align-items: center;
17085
- justify-content: center;
17086
- color: ${({ $color }) => $color ?? "currentColor"};
17087
- flex-shrink: 0;
17088
- `;
17089
-
17090
- // src/components/icon/icon.tsx
17091
- var import_jsx_runtime4 = require("react/jsx-runtime");
17092
- var DEFAULT_SIZE = 16;
17093
- var Icon = (0, import_react4.forwardRef)(function Icon2({ icon: IconComponent, size = DEFAULT_SIZE, color, className, "aria-label": ariaLabel }, ref) {
17094
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
17095
- IconWrapper,
17096
- {
17097
- ref,
17098
- className,
17099
- $color: color,
17100
- "aria-label": ariaLabel,
17101
- role: ariaLabel ? "img" : void 0,
17102
- children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(IconComponent, { width: size, height: size, strokeWidth: getStrokeWidth(size) })
17103
- }
17104
- );
17105
- });
17106
-
17107
- // src/components/icon-button/icon-button-styles.ts
17108
- var import_styled_components4 = __toESM(require("styled-components"), 1);
17109
- var PADDING = {
17110
- short: {
17111
- small: "4px",
17112
- medium: "6px"
17113
- },
17114
- long: {
17115
- small: "4px 6px",
17116
- medium: "6px 10px"
17117
- }
17118
- };
17119
- var BORDER_RADIUS = {
17120
- small: 6,
17121
- medium: 10
17122
- };
17123
- var Button = import_styled_components4.default.button`
17124
- display: inline-flex;
17125
- align-items: center;
17126
- justify-content: center;
17127
- padding: ${({ $size, $variant }) => PADDING[$variant][$size]};
17128
- border: none;
17129
- border-radius: ${({ $size }) => BORDER_RADIUS[$size]}px;
17130
- background: ${({ $isSelected }) => $isSelected ? colors["brown-40"] : "transparent"};
17131
- color: ${colors["brown-100"]};
17132
- cursor: pointer;
17133
- transition: background-color 100ms ease;
17134
-
17135
- &:hover {
17136
- background: ${({ $isSelected }) => $isSelected ? colors["brown-40"] : colors["brown-20"]};
17137
- }
17138
17633
 
17139
- &:active {
17140
- background: ${colors["brown-40"]};
17141
- }
17634
+ // src/components/chat/chat-message-actions/chat-message-actions.tsx
17635
+ var import_react11 = require("react");
17142
17636
 
17143
- &:disabled {
17144
- color: ${colors["brown-50"]};
17145
- background: transparent;
17146
- cursor: not-allowed;
17637
+ // src/components/chat/chat-context/chat-context.ts
17638
+ var import_react9 = require("react");
17639
+ var ChatContext = (0, import_react9.createContext)(null);
17640
+ function useChatContext() {
17641
+ const context = (0, import_react9.useContext)(ChatContext);
17642
+ if (!context) {
17643
+ throw new Error("useChatContext must be used within a Chat.Root component");
17147
17644
  }
17148
- `;
17149
-
17150
- // src/components/icon-button/icon-button.tsx
17151
- var import_jsx_runtime5 = require("react/jsx-runtime");
17152
- var ICON_SIZE_BY_BUTTON_SIZE = {
17153
- small: 16,
17154
- medium: 20
17155
- };
17156
- function IconButton({
17157
- icon,
17158
- size = "small",
17159
- variant = "short",
17160
- isSelected = false,
17161
- disabled = false,
17162
- onClick,
17163
- className,
17164
- id,
17165
- as,
17166
- "aria-label": ariaLabel
17167
- }) {
17168
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
17169
- Button,
17170
- {
17171
- className,
17172
- id,
17173
- as,
17174
- $size: size,
17175
- $variant: variant,
17176
- $isSelected: isSelected,
17177
- disabled,
17178
- onClick: disabled ? void 0 : onClick,
17179
- "aria-label": ariaLabel,
17180
- children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Icon, { icon, size: ICON_SIZE_BY_BUTTON_SIZE[size] })
17181
- }
17182
- );
17645
+ return context;
17183
17646
  }
17184
17647
 
17185
17648
  // src/components/chat/hooks/use-copy-to-clipboard.ts
17186
- var import_react5 = require("react");
17649
+ var import_react10 = require("react");
17187
17650
 
17188
17651
  // src/utils/clipboard.ts
17189
17652
  function copyToClipboard(text2, options) {
@@ -17193,37 +17656,18 @@ function copyToClipboard(text2, options) {
17193
17656
  );
17194
17657
  }
17195
17658
 
17196
- // src/utils/markdown-to-plain-text.ts
17197
- var PLACEHOLDER_PREFIX = "\u200B\u200BIC";
17198
- var PLACEHOLDER_SUFFIX = "\u200B\u200B";
17199
- var PLACEHOLDER_REGEX = new RegExp(`${PLACEHOLDER_PREFIX}(\\d+)${PLACEHOLDER_SUFFIX}`, "g");
17200
- function stripInlineMarks(text2) {
17201
- return text2.replace(/\*{3}(.+?)\*{3}/g, "$1").replace(/\*{2}(.+?)\*{2}/g, "$1").replace(/_{2}(.+?)_{2}/g, "$1").replace(/\*(.+?)\*/g, "$1").replace(/_(.+?)_/g, "$1").replace(/~~(.+?)~~/g, "$1");
17202
- }
17203
- function markdownToPlainText(markdown) {
17204
- if (!markdown) return "";
17205
- const inlineCodeSnippets = [];
17206
- let result = markdown.replace(/^<!--[\s\S]*?--!?>\n?/gm, "").replace(/```[\s\S]*?```/g, "").replace(/`([^`]+)`/g, (_match, code) => {
17207
- inlineCodeSnippets.push(code);
17208
- return `${PLACEHOLDER_PREFIX}${inlineCodeSnippets.length - 1}${PLACEHOLDER_SUFFIX}`;
17209
- }).replace(/^#{1,6}\s+/gm, "").replace(/^(?:---|\*\*\*|___)\s*$/gm, "");
17210
- 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();
17211
- result = result.replace(PLACEHOLDER_REGEX, (_match, index2) => inlineCodeSnippets[Number(index2)]);
17212
- return result;
17213
- }
17214
-
17215
17659
  // src/components/chat/hooks/use-copy-to-clipboard.ts
17216
17660
  var RESET_DELAY_MS = 2e3;
17217
17661
  function useCopyToClipboard() {
17218
- const [isCopied, setIsCopied] = (0, import_react5.useState)(false);
17219
- const timeoutRef = (0, import_react5.useRef)(null);
17220
- (0, import_react5.useEffect)(() => {
17662
+ const [isCopied, setIsCopied] = (0, import_react10.useState)(false);
17663
+ const timeoutRef = (0, import_react10.useRef)(null);
17664
+ (0, import_react10.useEffect)(() => {
17221
17665
  return () => {
17222
17666
  if (timeoutRef.current) clearTimeout(timeoutRef.current);
17223
17667
  };
17224
17668
  }, []);
17225
- const copy = (0, import_react5.useCallback)((text2) => {
17226
- copyToClipboard(markdownToPlainText(text2), {
17669
+ const copy = (0, import_react10.useCallback)((text2) => {
17670
+ copyToClipboard(text2, {
17227
17671
  onSuccess: () => {
17228
17672
  setIsCopied(true);
17229
17673
  if (timeoutRef.current) clearTimeout(timeoutRef.current);
@@ -17262,24 +17706,24 @@ var AnimatedAction = import_styled_components5.default.div`
17262
17706
  `;
17263
17707
 
17264
17708
  // src/components/chat/chat-message-actions/chat-message-actions.tsx
17265
- var import_jsx_runtime6 = require("react/jsx-runtime");
17709
+ var import_jsx_runtime13 = require("react/jsx-runtime");
17266
17710
  function ChatMessageActions({ className, messageId, content: content2, role, isHelpful }) {
17267
17711
  const { onCopyMessage, onThumbUpClick, onThumbDownClick } = useChatContext();
17268
17712
  const { isCopied, copy } = useCopyToClipboard();
17269
- const handleCopy = (0, import_react6.useCallback)(() => {
17713
+ const handleCopy = (0, import_react11.useCallback)(() => {
17270
17714
  copy(content2);
17271
17715
  onCopyMessage?.(messageId);
17272
17716
  }, [content2, messageId, copy, onCopyMessage]);
17273
- const handleThumbUp = (0, import_react6.useCallback)(() => {
17717
+ const handleThumbUp = (0, import_react11.useCallback)(() => {
17274
17718
  onThumbUpClick?.(messageId, isHelpful === true ? null : true);
17275
17719
  }, [messageId, isHelpful, onThumbUpClick]);
17276
- const handleThumbDown = (0, import_react6.useCallback)(() => {
17720
+ const handleThumbDown = (0, import_react11.useCallback)(() => {
17277
17721
  onThumbDownClick?.(messageId, isHelpful === false ? null : false);
17278
17722
  }, [messageId, isHelpful, onThumbDownClick]);
17279
17723
  const isAssistant = role === MESSAGE_ROLE.ASSISTANT;
17280
17724
  const hasFeedback = isAssistant && (onThumbUpClick || onThumbDownClick);
17281
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(ActionsContainer, { $role: role, className, children: [
17282
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
17725
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(ActionsContainer, { $role: role, className, children: [
17726
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
17283
17727
  IconButton,
17284
17728
  {
17285
17729
  icon: isCopied ? IconCheck : IconCopy,
@@ -17287,7 +17731,7 @@ function ChatMessageActions({ className, messageId, content: content2, role, isH
17287
17731
  "aria-label": isCopied ? "Copied" : "Copy message"
17288
17732
  }
17289
17733
  ),
17290
- hasFeedback && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(AnimatedAction, { $visible: isHelpful !== false, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
17734
+ hasFeedback && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(AnimatedAction, { $visible: isHelpful !== false, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
17291
17735
  IconButton,
17292
17736
  {
17293
17737
  icon: IconThumbUp,
@@ -17296,7 +17740,7 @@ function ChatMessageActions({ className, messageId, content: content2, role, isH
17296
17740
  "aria-label": "Good response"
17297
17741
  }
17298
17742
  ) }),
17299
- hasFeedback && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(AnimatedAction, { $visible: isHelpful !== true, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
17743
+ hasFeedback && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(AnimatedAction, { $visible: isHelpful !== true, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
17300
17744
  IconButton,
17301
17745
  {
17302
17746
  icon: IconThumbDown,
@@ -17334,26 +17778,28 @@ var MessageBubble = import_styled_components6.default.div`
17334
17778
  padding: ${({ $role }) => $role === MESSAGE_ROLE.USER ? "12px 16px" : "0"};
17335
17779
  border-radius: ${({ $role }) => $role === MESSAGE_ROLE.USER ? "16px" : "0"};
17336
17780
  max-width: 100%;
17781
+ width: 100%;
17337
17782
  min-width: 0;
17783
+ /* Vertical rhythm between consecutive blocks — renderer-owned, not agent-emitted.
17784
+ * Industry standard (Claude, ChatGPT, Vercel AI Elements): the chat renderer applies
17785
+ * spacing via CSS; agents emit semantic blocks only. Value matches the MessageContainer
17786
+ * gap. TODO: replace 12px with a clay spacing token when a spacing scale is added. */
17787
+ display: flex;
17788
+ flex-direction: column;
17789
+ gap: 12px;
17338
17790
  `;
17339
17791
 
17340
17792
  // src/components/chat/chat-message/chat-message.tsx
17341
- var import_jsx_runtime7 = require("react/jsx-runtime");
17793
+ var import_jsx_runtime14 = require("react/jsx-runtime");
17342
17794
  function ChatMessage({ message }) {
17343
- const { TableActions: ChatTableActions } = useChatContext();
17344
- const blockContextValue = (0, import_react7.useMemo)(
17345
- () => ({
17346
- TableActions: ChatTableActions ? ({ tableIndex }) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ChatTableActions, { messageId: message.id, tableIndex }) : void 0
17347
- }),
17348
- [ChatTableActions, message.id]
17349
- );
17350
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(MessageRow, { $role: message.role, role: "article", "aria-label": `${message.role} message`, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(MessageContainer, { $role: message.role, children: [
17351
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(MessageBubble, { $role: message.role, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(BlockContext.Provider, { value: blockContextValue, children: message.blocks.map((block, index2) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Block, { block }, index2)) }) }),
17352
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
17795
+ const clipboardText = (0, import_react12.useMemo)(() => copyMessageText(message), [message]);
17796
+ 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: [
17797
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(MessageBubble, { $role: message.role, "data-testid": "message-bubble", children: message.blocks.map((block, index2) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Block, { block }, index2)) }),
17798
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
17353
17799
  ChatMessageActions,
17354
17800
  {
17355
17801
  messageId: message.id,
17356
- content: message.fallbackText,
17802
+ content: clipboardText,
17357
17803
  role: message.role,
17358
17804
  isHelpful: message.isHelpful
17359
17805
  }
@@ -17398,20 +17844,20 @@ var Key = import_styled_components7.default.kbd`
17398
17844
  `;
17399
17845
 
17400
17846
  // src/components/keyboard-shortcut/keyboard-shortcut.tsx
17401
- var import_jsx_runtime8 = require("react/jsx-runtime");
17847
+ var import_jsx_runtime15 = require("react/jsx-runtime");
17402
17848
  function KeyboardShortcut({
17403
17849
  keys,
17404
17850
  variant = KEYBOARD_SHORTCUT_VARIANTS.OUTLINED,
17405
17851
  className
17406
17852
  }) {
17407
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Wrapper, { className, children: keys.map((KeyComponent, index2) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Key, { $variant: variant, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Icon, { icon: KeyComponent, size: 12 }) }, index2)) });
17853
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Wrapper, { className, children: keys.map((KeyComponent, index2) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Key, { $variant: variant, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Icon, { icon: KeyComponent, size: 12 }) }, index2)) });
17408
17854
  }
17409
17855
 
17410
17856
  // src/components/keyboard-shortcut/icons/icon-ctrl.tsx
17411
- var import_jsx_runtime9 = require("react/jsx-runtime");
17857
+ var import_jsx_runtime16 = require("react/jsx-runtime");
17412
17858
  function IconCtrl(props) {
17413
17859
  const { width: width2 = 24, height: height2 = 24, ...rest } = props;
17414
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", width: width2, height: height2, fill: "none", ...rest, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
17860
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", width: width2, height: height2, fill: "none", ...rest, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
17415
17861
  "text",
17416
17862
  {
17417
17863
  x: "50%",
@@ -17428,7 +17874,7 @@ function IconCtrl(props) {
17428
17874
  }
17429
17875
 
17430
17876
  // src/components/lottie/lottie.tsx
17431
- var import_react9 = require("react");
17877
+ var import_react14 = require("react");
17432
17878
 
17433
17879
  // src/components/lottie/lottie-styles.ts
17434
17880
  var import_styled_components8 = __toESM(require("styled-components"), 1);
@@ -17444,7 +17890,7 @@ var LottieContainer = import_styled_components8.default.div`
17444
17890
  `;
17445
17891
 
17446
17892
  // src/components/lottie/use-lottie.ts
17447
- var import_react8 = require("react");
17893
+ var import_react13 = require("react");
17448
17894
  var import_lottie_web = __toESM(require_lottie(), 1);
17449
17895
  function useLottie({
17450
17896
  animationData: animationData2,
@@ -17459,17 +17905,17 @@ function useLottie({
17459
17905
  onLoopComplete,
17460
17906
  onEnterFrame
17461
17907
  }) {
17462
- const containerRef = (0, import_react8.useRef)(null);
17463
- const animationRef = (0, import_react8.useRef)(null);
17464
- const onAnimationLoadedRef = (0, import_react8.useRef)(onAnimationLoaded);
17465
- const onCompleteRef = (0, import_react8.useRef)(onComplete);
17466
- const onLoopCompleteRef = (0, import_react8.useRef)(onLoopComplete);
17467
- const onEnterFrameRef = (0, import_react8.useRef)(onEnterFrame);
17908
+ const containerRef = (0, import_react13.useRef)(null);
17909
+ const animationRef = (0, import_react13.useRef)(null);
17910
+ const onAnimationLoadedRef = (0, import_react13.useRef)(onAnimationLoaded);
17911
+ const onCompleteRef = (0, import_react13.useRef)(onComplete);
17912
+ const onLoopCompleteRef = (0, import_react13.useRef)(onLoopComplete);
17913
+ const onEnterFrameRef = (0, import_react13.useRef)(onEnterFrame);
17468
17914
  onAnimationLoadedRef.current = onAnimationLoaded;
17469
17915
  onCompleteRef.current = onComplete;
17470
17916
  onLoopCompleteRef.current = onLoopComplete;
17471
17917
  onEnterFrameRef.current = onEnterFrame;
17472
- (0, import_react8.useEffect)(() => {
17918
+ (0, import_react13.useEffect)(() => {
17473
17919
  if (!containerRef.current) return;
17474
17920
  const anim = import_lottie_web.default.loadAnimation({
17475
17921
  container: containerRef.current,
@@ -17561,8 +18007,8 @@ function createLottieRef(animationRef) {
17561
18007
  }
17562
18008
 
17563
18009
  // src/components/lottie/lottie.tsx
17564
- var import_jsx_runtime10 = require("react/jsx-runtime");
17565
- var Lottie = (0, import_react9.forwardRef)(function Lottie2({
18010
+ var import_jsx_runtime17 = require("react/jsx-runtime");
18011
+ var Lottie = (0, import_react14.forwardRef)(function Lottie2({
17566
18012
  animationData: animationData2,
17567
18013
  autoplay,
17568
18014
  loop,
@@ -17592,8 +18038,8 @@ var Lottie = (0, import_react9.forwardRef)(function Lottie2({
17592
18038
  onLoopComplete,
17593
18039
  onEnterFrame
17594
18040
  });
17595
- (0, import_react9.useImperativeHandle)(ref, () => createLottieRef(animationRef), [animationRef]);
17596
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
18041
+ (0, import_react14.useImperativeHandle)(ref, () => createLottieRef(animationRef), [animationRef]);
18042
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
17597
18043
  LottieContainer,
17598
18044
  {
17599
18045
  ref: containerRef,
@@ -17607,7 +18053,7 @@ var Lottie = (0, import_react9.forwardRef)(function Lottie2({
17607
18053
  });
17608
18054
 
17609
18055
  // src/components/text-area/text-area.tsx
17610
- var import_react10 = require("react");
18056
+ var import_react15 = require("react");
17611
18057
 
17612
18058
  // src/utils/merge-refs.ts
17613
18059
  function mergeRefs(...refs) {
@@ -17647,23 +18093,23 @@ var StyledTextArea = import_styled_components9.default.textarea`
17647
18093
  `;
17648
18094
 
17649
18095
  // src/components/text-area/text-area.tsx
17650
- var import_jsx_runtime11 = require("react/jsx-runtime");
17651
- var TextArea = (0, import_react10.forwardRef)(function TextArea2({ value: value2, onChange, placeholder, maxHeight, rows = 1, autoFocus, onKeyDown, disabled, className }, externalRef) {
17652
- const internalRef = (0, import_react10.useRef)(null);
17653
- (0, import_react10.useLayoutEffect)(() => {
18096
+ var import_jsx_runtime18 = require("react/jsx-runtime");
18097
+ var TextArea = (0, import_react15.forwardRef)(function TextArea2({ value: value2, onChange, placeholder, maxHeight, rows = 1, autoFocus, onKeyDown, disabled, className }, externalRef) {
18098
+ const internalRef = (0, import_react15.useRef)(null);
18099
+ (0, import_react15.useLayoutEffect)(() => {
17654
18100
  const el = internalRef.current;
17655
18101
  if (el) {
17656
18102
  el.style.height = "auto";
17657
18103
  el.style.height = `${el.scrollHeight}px`;
17658
18104
  }
17659
18105
  }, [value2]);
17660
- const handleChange = (0, import_react10.useCallback)(
18106
+ const handleChange = (0, import_react15.useCallback)(
17661
18107
  (e) => {
17662
18108
  onChange(e.target.value);
17663
18109
  },
17664
18110
  [onChange]
17665
18111
  );
17666
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
18112
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
17667
18113
  StyledTextArea,
17668
18114
  {
17669
18115
  ref: mergeRefs(internalRef, externalRef),
@@ -17733,21 +18179,26 @@ function parseMarkdownTable(markdown, tableIndex = 0) {
17733
18179
  return null;
17734
18180
  }
17735
18181
 
17736
- // src/utils/convert-table-to-csv.ts
17737
- function escapeField(field) {
17738
- if (field.includes(",") || field.includes('"') || field.includes("\n") || field.includes("\r")) {
17739
- return `"${field.replace(/"/g, '""')}"`;
18182
+ // src/utils/escape-csv-field.ts
18183
+ var FORMULA_TRIGGERS = /* @__PURE__ */ new Set(["=", "+", "@", " ", "\r"]);
18184
+ function escapeCsvField(field) {
18185
+ const needsNeutralise = field.length > 0 && FORMULA_TRIGGERS.has(field[0]);
18186
+ const safeField = needsNeutralise ? ` ${field}` : field;
18187
+ if (needsNeutralise || safeField.includes(",") || safeField.includes('"') || safeField.includes("\n") || safeField.includes("\r")) {
18188
+ return `"${safeField.replace(/"/g, '""')}"`;
17740
18189
  }
17741
- return field;
18190
+ return safeField;
17742
18191
  }
18192
+
18193
+ // src/utils/convert-table-to-csv.ts
17743
18194
  function convertTableToCSV(table) {
17744
- const headerRow = table.columns.map(escapeField).join(",");
17745
- const dataRows = table.rows.map((row) => row.map(escapeField).join(","));
18195
+ const headerRow = table.columns.map(escapeCsvField).join(",");
18196
+ const dataRows = table.rows.map((row) => row.map(escapeCsvField).join(","));
17746
18197
  return [headerRow, ...dataRows].join("\r\n");
17747
18198
  }
17748
18199
 
17749
18200
  // src/components/button/button.tsx
17750
- var import_react11 = require("react");
18201
+ var import_react16 = require("react");
17751
18202
 
17752
18203
  // src/components/button/button-styles.ts
17753
18204
  var import_styled_components10 = __toESM(require("styled-components"), 1);
@@ -17869,8 +18320,8 @@ var StyledButton = import_styled_components10.default.button`
17869
18320
  `;
17870
18321
 
17871
18322
  // src/components/button/button.tsx
17872
- var import_jsx_runtime12 = require("react/jsx-runtime");
17873
- var Button2 = (0, import_react11.forwardRef)(function Button3({
18323
+ var import_jsx_runtime19 = require("react/jsx-runtime");
18324
+ var Button2 = (0, import_react16.forwardRef)(function Button3({
17874
18325
  children,
17875
18326
  variant = BUTTON_VARIANT.SOLID,
17876
18327
  intent = BUTTON_INTENT.PRIMARY,
@@ -17882,8 +18333,8 @@ var Button2 = (0, import_react11.forwardRef)(function Button3({
17882
18333
  className,
17883
18334
  "aria-label": ariaLabel
17884
18335
  }, ref) {
17885
- const iconElement = icon ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Icon, { icon, size: 16 }) : null;
17886
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
18336
+ const iconElement = icon ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Icon, { icon, size: 16 }) : null;
18337
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
17887
18338
  StyledButton,
17888
18339
  {
17889
18340
  ref,
@@ -17897,7 +18348,7 @@ var Button2 = (0, import_react11.forwardRef)(function Button3({
17897
18348
  "aria-label": ariaLabel,
17898
18349
  children: [
17899
18350
  iconPosition === ICON_POSITION.LEADING && iconElement,
17900
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { children }),
18351
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { children }),
17901
18352
  iconPosition === ICON_POSITION.TRAILING && iconElement
17902
18353
  ]
17903
18354
  }
@@ -17905,6 +18356,10 @@ var Button2 = (0, import_react11.forwardRef)(function Button3({
17905
18356
  });
17906
18357
  // Annotate the CommonJS export names for ESM import in node:
17907
18358
  0 && (module.exports = {
18359
+ ArtifactDownloadButton,
18360
+ ArtifactRefBlock,
18361
+ ArtifactRefBlockService,
18362
+ ArtifactServices,
17908
18363
  BUTTON_INTENT,
17909
18364
  BUTTON_SIZE,
17910
18365
  BUTTON_VARIANT,
@@ -17922,12 +18377,18 @@ var Button2 = (0, import_react11.forwardRef)(function Button3({
17922
18377
  KeyboardShortcut,
17923
18378
  Lottie,
17924
18379
  MESSAGE_ROLE,
18380
+ TableArtifact,
17925
18381
  TextArea,
17926
18382
  TextBlockService,
18383
+ artifactToClipboardText,
18384
+ artifactTypes,
17927
18385
  blockTypes,
17928
18386
  colors,
17929
18387
  convertTableToCSV,
18388
+ copyMessageText,
18389
+ escapeCsvField,
17930
18390
  fontFamilies,
18391
+ isSafeDownloadUrl,
17931
18392
  markdownToPlainText,
17932
18393
  mergeRefs,
17933
18394
  parseMarkdownTable,
@@ -17954,6 +18415,7 @@ lottie-web/build/player/lottie.js:
17954
18415
  @tabler/icons-react/dist/esm/createReactComponent.mjs:
17955
18416
  @tabler/icons-react/dist/esm/icons/IconCheck.mjs:
17956
18417
  @tabler/icons-react/dist/esm/icons/IconCopy.mjs:
18418
+ @tabler/icons-react/dist/esm/icons/IconDownload.mjs:
17957
18419
  @tabler/icons-react/dist/esm/icons/IconThumbDown.mjs:
17958
18420
  @tabler/icons-react/dist/esm/icons/IconThumbUp.mjs:
17959
18421
  @tabler/icons-react/dist/esm/tabler-icons-react.mjs: