@shapesos/clay 0.12.0 → 0.13.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 (49) hide show
  1. package/dist/artifacts.cjs +695 -246
  2. package/dist/artifacts.cjs.map +1 -1
  3. package/dist/artifacts.d.cts +133 -5
  4. package/dist/artifacts.d.ts +133 -5
  5. package/dist/artifacts.js +7 -2
  6. package/dist/blocks.cjs +705 -259
  7. package/dist/blocks.cjs.map +1 -1
  8. package/dist/blocks.css +1 -1
  9. package/dist/blocks.d.cts +8 -10
  10. package/dist/blocks.d.ts +8 -10
  11. package/dist/blocks.js +3 -2
  12. package/dist/capture.cjs +220 -0
  13. package/dist/capture.cjs.map +1 -0
  14. package/dist/capture.d.cts +113 -0
  15. package/dist/capture.d.ts +113 -0
  16. package/dist/capture.js +17 -0
  17. package/dist/chat.cjs +724 -287
  18. package/dist/chat.cjs.map +1 -1
  19. package/dist/chat.d.cts +2 -2
  20. package/dist/chat.d.ts +2 -2
  21. package/dist/chat.js +4 -3
  22. package/dist/{chunk-36CB624W.js → chunk-26XVDY6Z.js} +3 -3
  23. package/dist/chunk-26XVDY6Z.js.map +1 -0
  24. package/dist/{chunk-Z5JWF24N.js → chunk-DT44KNCV.js} +367 -82
  25. package/dist/chunk-DT44KNCV.js.map +1 -0
  26. package/dist/chunk-JAGNXEWK.js +116 -0
  27. package/dist/chunk-JAGNXEWK.js.map +1 -0
  28. package/dist/chunk-LXYCT4YO.js +7 -0
  29. package/dist/chunk-LXYCT4YO.js.map +1 -0
  30. package/dist/{chunk-FFX3CAOX.js → chunk-QH2WW2MW.js} +3 -3
  31. package/dist/chunk-RSRNSWJN.js +1 -0
  32. package/dist/chunk-RSRNSWJN.js.map +1 -0
  33. package/dist/dom-to-image-NATF3446.js +21 -0
  34. package/dist/dom-to-image-NATF3446.js.map +1 -0
  35. package/dist/index.cjs +763 -317
  36. package/dist/index.cjs.map +1 -1
  37. package/dist/index.d.cts +3 -3
  38. package/dist/index.d.ts +3 -3
  39. package/dist/index.js +9 -4
  40. package/dist/{types-C0BvwliI.d.cts → types-BzXPBy6s.d.cts} +39 -41
  41. package/dist/{types-uPfn67Dc.d.ts → types-CW45GBXl.d.cts} +25 -13
  42. package/dist/{types-DCutaXjZ.d.cts → types-D1b8v7EY.d.ts} +25 -13
  43. package/dist/{types-C5bFH4v3.d.ts → types-GCkXbyFm.d.ts} +39 -41
  44. package/package.json +12 -2
  45. package/dist/chunk-36CB624W.js.map +0 -1
  46. package/dist/chunk-AQEJRMRN.js +0 -1
  47. package/dist/chunk-Z5JWF24N.js.map +0 -1
  48. /package/dist/{chunk-AQEJRMRN.js.map → capture.js.map} +0 -0
  49. /package/dist/{chunk-FFX3CAOX.js.map → chunk-QH2WW2MW.js.map} +0 -0
@@ -1,3 +1,6 @@
1
+ import {
2
+ CAPTURE_EXCLUDE_ATTRIBUTE
3
+ } from "./chunk-LXYCT4YO.js";
1
4
  import {
2
5
  BarChart,
3
6
  LineChart,
@@ -27,7 +30,10 @@ var artifactTypes = {
27
30
 
28
31
  // src/components/artifacts/chart-artifact/types.ts
29
32
  var DEFAULT_CHART_ARTIFACT_LABELS = {
30
- download: "Download CSV",
33
+ download: "Download",
34
+ downloadCsv: "Download CSV",
35
+ downloadPng: "Download PNG",
36
+ copyAsImage: "Copy Image",
31
37
  unavailable: "Chart data is unavailable.",
32
38
  loadError: "Couldn't load chart data.",
33
39
  loading: "Loading chart\u2026",
@@ -100,25 +106,44 @@ var createReactComponent = (type, iconName, iconNamePascal, iconNode) => {
100
106
  return Component;
101
107
  };
102
108
 
109
+ // node_modules/@tabler/icons-react/dist/esm/icons/IconAlertCircle.mjs
110
+ var __iconNode = [["path", { "d": "M3 12a9 9 0 1 0 18 0a9 9 0 0 0 -18 0", "key": "svg-0" }], ["path", { "d": "M12 8v4", "key": "svg-1" }], ["path", { "d": "M12 16h.01", "key": "svg-2" }]];
111
+ var IconAlertCircle = createReactComponent("outline", "alert-circle", "AlertCircle", __iconNode);
112
+
103
113
  // node_modules/@tabler/icons-react/dist/esm/icons/IconCheck.mjs
104
- var __iconNode = [["path", { "d": "M5 12l5 5l10 -10", "key": "svg-0" }]];
105
- var IconCheck = createReactComponent("outline", "check", "Check", __iconNode);
114
+ var __iconNode2 = [["path", { "d": "M5 12l5 5l10 -10", "key": "svg-0" }]];
115
+ var IconCheck = createReactComponent("outline", "check", "Check", __iconNode2);
106
116
 
107
117
  // node_modules/@tabler/icons-react/dist/esm/icons/IconCopy.mjs
108
- 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" }]];
109
- var IconCopy = createReactComponent("outline", "copy", "Copy", __iconNode2);
118
+ var __iconNode3 = [["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" }]];
119
+ var IconCopy = createReactComponent("outline", "copy", "Copy", __iconNode3);
110
120
 
111
121
  // node_modules/@tabler/icons-react/dist/esm/icons/IconDownload.mjs
112
- 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" }]];
113
- var IconDownload = createReactComponent("outline", "download", "Download", __iconNode3);
122
+ var __iconNode4 = [["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" }]];
123
+ var IconDownload = createReactComponent("outline", "download", "Download", __iconNode4);
124
+
125
+ // node_modules/@tabler/icons-react/dist/esm/icons/IconFileSpreadsheet.mjs
126
+ var __iconNode5 = [["path", { "d": "M14 3v4a1 1 0 0 0 1 1h4", "key": "svg-0" }], ["path", { "d": "M17 21h-10a2 2 0 0 1 -2 -2v-14a2 2 0 0 1 2 -2h7l5 5v11a2 2 0 0 1 -2 2", "key": "svg-1" }], ["path", { "d": "M8 11h8v7h-8l0 -7", "key": "svg-2" }], ["path", { "d": "M8 15h8", "key": "svg-3" }], ["path", { "d": "M11 11v7", "key": "svg-4" }]];
127
+ var IconFileSpreadsheet = createReactComponent("outline", "file-spreadsheet", "FileSpreadsheet", __iconNode5);
128
+
129
+ // node_modules/@tabler/icons-react/dist/esm/icons/IconLoader2.mjs
130
+ var __iconNode6 = [["path", { "d": "M12 3a9 9 0 1 0 9 9", "key": "svg-0" }]];
131
+ var IconLoader2 = createReactComponent("outline", "loader-2", "Loader2", __iconNode6);
132
+
133
+ // node_modules/@tabler/icons-react/dist/esm/icons/IconPhoto.mjs
134
+ var __iconNode7 = [["path", { "d": "M15 8h.01", "key": "svg-0" }], ["path", { "d": "M3 6a3 3 0 0 1 3 -3h12a3 3 0 0 1 3 3v12a3 3 0 0 1 -3 3h-12a3 3 0 0 1 -3 -3v-12", "key": "svg-1" }], ["path", { "d": "M3 16l5 -5c.928 -.893 2.072 -.893 3 0l5 5", "key": "svg-2" }], ["path", { "d": "M14 14l1 -1c.928 -.893 2.072 -.893 3 0l3 3", "key": "svg-3" }]];
135
+ var IconPhoto = createReactComponent("outline", "photo", "Photo", __iconNode7);
114
136
 
115
137
  // node_modules/@tabler/icons-react/dist/esm/icons/IconThumbDown.mjs
116
- 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" }]];
117
- var IconThumbDown = createReactComponent("outline", "thumb-down", "ThumbDown", __iconNode4);
138
+ var __iconNode8 = [["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" }]];
139
+ var IconThumbDown = createReactComponent("outline", "thumb-down", "ThumbDown", __iconNode8);
118
140
 
119
141
  // node_modules/@tabler/icons-react/dist/esm/icons/IconThumbUp.mjs
120
- 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" }]];
121
- var IconThumbUp = createReactComponent("outline", "thumb-up", "ThumbUp", __iconNode5);
142
+ var __iconNode9 = [["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" }]];
143
+ var IconThumbUp = createReactComponent("outline", "thumb-up", "ThumbUp", __iconNode9);
144
+
145
+ // src/components/artifacts/artifact-copy-as-image-button/artifact-copy-as-image-button.tsx
146
+ import { useCallback as useCallback2 } from "react";
122
147
 
123
148
  // src/components/ui/tooltip.tsx
124
149
  import * as React from "react";
@@ -141,17 +166,119 @@ var TooltipContent = React.forwardRef(({ className, sideOffset = 4, ...props },
141
166
  ));
142
167
  TooltipContent.displayName = TooltipPrimitive.Content.displayName;
143
168
 
144
- // src/components/artifacts/artifact-download-button/artifact-download-button.tsx
169
+ // src/components/artifacts/artifact-copy-as-image-button/use-copy-as-image.ts
170
+ import { useCallback, useEffect, useRef, useState } from "react";
171
+ var DEFAULT_RESET_AFTER_MS = 1500;
172
+ function useCopyAsImage(targetRef, options = {}) {
173
+ const [status, setStatus] = useState("idle");
174
+ const [error, setError] = useState(null);
175
+ const resetTimerRef = useRef(null);
176
+ useEffect(() => {
177
+ return () => {
178
+ if (resetTimerRef.current !== null) clearTimeout(resetTimerRef.current);
179
+ };
180
+ }, []);
181
+ const copy = useCallback(async () => {
182
+ if (resetTimerRef.current !== null) {
183
+ clearTimeout(resetTimerRef.current);
184
+ resetTimerRef.current = null;
185
+ }
186
+ const { resetAfterMs = DEFAULT_RESET_AFTER_MS, ...nodeOptions } = options;
187
+ const scheduleReset = () => {
188
+ if (resetAfterMs <= 0) return;
189
+ resetTimerRef.current = setTimeout(() => {
190
+ resetTimerRef.current = null;
191
+ setStatus("idle");
192
+ setError(null);
193
+ }, resetAfterMs);
194
+ };
195
+ const node = targetRef.current;
196
+ if (!node) {
197
+ const err = new Error("ArtifactCopyAsImageButton: target ref is not attached to a DOM node.");
198
+ setError(err);
199
+ setStatus("error");
200
+ scheduleReset();
201
+ throw err;
202
+ }
203
+ setError(null);
204
+ setStatus("working");
205
+ try {
206
+ const { copyNodeAsPng } = await import("./dom-to-image-NATF3446.js");
207
+ await copyNodeAsPng(node, nodeOptions);
208
+ setStatus("success");
209
+ scheduleReset();
210
+ } catch (err) {
211
+ const wrapped = err instanceof Error ? err : new Error(String(err));
212
+ setError(wrapped);
213
+ setStatus("error");
214
+ scheduleReset();
215
+ throw wrapped;
216
+ }
217
+ }, [targetRef, options]);
218
+ return { status, error, copy };
219
+ }
220
+
221
+ // src/components/artifacts/artifact-copy-as-image-button/artifact-copy-as-image-button.tsx
145
222
  import { jsx as jsx2, jsxs } from "react/jsx-runtime";
146
- var DEFAULT_LABEL = "Download CSV";
147
- function ArtifactDownloadButton({
148
- href,
223
+ var MAX_ERROR_MESSAGE_CHARS = 120;
224
+ function truncateErrorMessage(message, max = MAX_ERROR_MESSAGE_CHARS) {
225
+ return message.length <= max ? message : `${message.slice(0, max - 1)}\u2026`;
226
+ }
227
+ var DEFAULT_LABEL = "Copy Image";
228
+ var ICON_BY_STATUS = {
229
+ // `IconCopy` reads as the clipboard-copy affordance (action-shaped), distinct from the
230
+ // download menu's format-shaped icons. The button only ever puts bytes on the clipboard, so
231
+ // the visual signal stays consistent across copy-aware artifact types.
232
+ idle: IconCopy,
233
+ working: IconLoader2,
234
+ success: IconCheck,
235
+ error: IconAlertCircle
236
+ };
237
+ var TRANSIENT_TOOLTIP_BY_STATUS = {
238
+ working: "Copying\u2026",
239
+ success: "Copied",
240
+ error: "Copy failed"
241
+ };
242
+ function ArtifactCopyAsImageButton({
243
+ targetRef,
149
244
  label = DEFAULT_LABEL,
245
+ disabled = false,
150
246
  onClick
151
247
  }) {
152
- if (!isSafeDownloadUrl(href)) return null;
248
+ const { status, error, copy } = useCopyAsImage(targetRef);
249
+ const handleClick = useCallback2(() => {
250
+ onClick?.();
251
+ copy().catch((err) => {
252
+ console.warn("[clay] ArtifactCopyAsImageButton: copy failed", err);
253
+ });
254
+ }, [copy, onClick]);
255
+ const statusLabel = status === "idle" ? label : status === "error" && error ? `Copy failed: ${truncateErrorMessage(error.message)}` : TRANSIENT_TOOLTIP_BY_STATUS[status] ?? label;
153
256
  return /* @__PURE__ */ jsx2(TooltipProvider, { children: /* @__PURE__ */ jsxs(Tooltip, { children: [
154
257
  /* @__PURE__ */ jsx2(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx2(
258
+ IconButton,
259
+ {
260
+ icon: ICON_BY_STATUS[status],
261
+ size: "small",
262
+ onClick: handleClick,
263
+ disabled: disabled || status === "working",
264
+ "aria-label": statusLabel
265
+ }
266
+ ) }),
267
+ /* @__PURE__ */ jsx2(TooltipContent, { children: statusLabel })
268
+ ] }) });
269
+ }
270
+
271
+ // src/components/artifacts/artifact-download-button/artifact-download-button.tsx
272
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
273
+ var DEFAULT_LABEL2 = "Download CSV";
274
+ function ArtifactDownloadButton({
275
+ href,
276
+ label = DEFAULT_LABEL2,
277
+ onClick
278
+ }) {
279
+ if (!isSafeDownloadUrl(href)) return null;
280
+ return /* @__PURE__ */ jsx3(TooltipProvider, { children: /* @__PURE__ */ jsxs2(Tooltip, { children: [
281
+ /* @__PURE__ */ jsx3(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx3(
155
282
  IconButton,
156
283
  {
157
284
  as: "a",
@@ -164,7 +291,7 @@ function ArtifactDownloadButton({
164
291
  "aria-label": label
165
292
  }
166
293
  ) }),
167
- /* @__PURE__ */ jsx2(TooltipContent, { children: label })
294
+ /* @__PURE__ */ jsx3(TooltipContent, { children: label })
168
295
  ] }) });
169
296
  }
170
297
  function isSafeDownloadUrl(url) {
@@ -176,22 +303,135 @@ function isSafeDownloadUrl(url) {
176
303
  }
177
304
  }
178
305
 
306
+ // src/components/artifacts/artifact-download-menu/artifact-download-menu.tsx
307
+ import { useCallback as useCallback3 } from "react";
308
+
309
+ // src/components/ui/dropdown-menu.tsx
310
+ import * as React2 from "react";
311
+ import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
312
+ import { jsx as jsx4 } from "react/jsx-runtime";
313
+ var DropdownMenu = DropdownMenuPrimitive.Root;
314
+ var DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
315
+ var DropdownMenuContent = React2.forwardRef(({ className, sideOffset = 4, ...props }, ref) => /* @__PURE__ */ jsx4(DropdownMenuPrimitive.Portal, { children: /* @__PURE__ */ jsx4(
316
+ DropdownMenuPrimitive.Content,
317
+ {
318
+ ref,
319
+ sideOffset,
320
+ className: cn(
321
+ // Stacking is consumer-configurable via the `--z-popover` CSS variable (default `99999`,
322
+ // declared in clay's generated `_theme-tokens.css` :root block). Consumers whose chat or
323
+ // modal layer sits even higher override on their own root once:
324
+ // :root { --z-popover: 9999999; }
325
+ // and every clay floating surface picks the new value up via cascade. The fallback in
326
+ // `var(...,99999)` only kicks in if the consumer also strips the clay stylesheet — which
327
+ // they shouldn't, but the safety net costs nothing.
328
+ "z-[var(--z-popover,99999)] min-w-[10rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md",
329
+ "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95",
330
+ "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",
331
+ className
332
+ ),
333
+ ...props
334
+ }
335
+ ) }));
336
+ DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName;
337
+ var DropdownMenuItem = React2.forwardRef(({ className, inset, ...props }, ref) => /* @__PURE__ */ jsx4(
338
+ DropdownMenuPrimitive.Item,
339
+ {
340
+ ref,
341
+ className: cn(
342
+ "relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors",
343
+ "focus:bg-accent focus:text-accent-foreground",
344
+ "data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
345
+ "[&_svg]:size-4 [&_svg]:shrink-0",
346
+ inset && "pl-8",
347
+ className
348
+ ),
349
+ ...props
350
+ }
351
+ ));
352
+ DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName;
353
+ var DropdownMenuSeparator = React2.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx4(DropdownMenuPrimitive.Separator, { ref, className: cn("-mx-1 my-1 h-px bg-muted", className), ...props }));
354
+ DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName;
355
+ var DropdownMenuLabel = React2.forwardRef(({ className, inset, ...props }, ref) => /* @__PURE__ */ jsx4(
356
+ DropdownMenuPrimitive.Label,
357
+ {
358
+ ref,
359
+ className: cn("px-2 py-1.5 text-sm font-semibold", inset && "pl-8", className),
360
+ ...props
361
+ }
362
+ ));
363
+ DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName;
364
+
365
+ // src/components/artifacts/artifact-download-menu/artifact-download-menu.tsx
366
+ import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
367
+ var DEFAULT_TRIGGER_LABEL = "Download";
368
+ var DEFAULT_CSV_LABEL = "Download CSV";
369
+ var DEFAULT_PNG_LABEL = "Download PNG";
370
+ var DEFAULT_PNG_BASENAME = "artifact";
371
+ function ArtifactDownloadMenu({
372
+ actions,
373
+ triggerLabel = DEFAULT_TRIGGER_LABEL
374
+ }) {
375
+ const { csv, png } = actions;
376
+ const showCsv = csv !== void 0 && isSafeDownloadUrl(csv.href);
377
+ const showPng = png !== void 0;
378
+ const handlePngSelect = useCallback3(async () => {
379
+ if (!png) return;
380
+ png.onSelect?.();
381
+ const node = png.targetRef.current;
382
+ if (!node) return;
383
+ const filename = `${png.filename ?? DEFAULT_PNG_BASENAME}.png`;
384
+ try {
385
+ const { downloadNodeAsPng } = await import("./dom-to-image-NATF3446.js");
386
+ await downloadNodeAsPng(node, filename);
387
+ } catch (err) {
388
+ console.warn("[clay] ArtifactDownloadMenu: PNG download failed", err);
389
+ }
390
+ }, [png]);
391
+ return /* @__PURE__ */ jsxs3(DropdownMenu, { children: [
392
+ /* @__PURE__ */ jsx5(TooltipProvider, { children: /* @__PURE__ */ jsxs3(Tooltip, { children: [
393
+ /* @__PURE__ */ jsx5(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx5(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx5(IconButton, { icon: IconDownload, size: "small", "aria-label": triggerLabel }) }) }),
394
+ /* @__PURE__ */ jsx5(TooltipContent, { children: triggerLabel })
395
+ ] }) }),
396
+ /* @__PURE__ */ jsxs3(DropdownMenuContent, { align: "end", children: [
397
+ showCsv ? (
398
+ // `asChild` lets Radix render the item as our anchor — the browser handles the
399
+ // download natively and respects `Content-Disposition`. The analytics callback rides
400
+ // on the anchor's own `onClick` (rather than Radix's `onSelect`) so the same handler
401
+ // fires whether the user mouse-clicks the item, hits Enter on it, or activates it via
402
+ // touch — `onSelect` is reliable only for keyboard activation when `asChild` is in use.
403
+ /* @__PURE__ */ jsx5(DropdownMenuItem, { asChild: true, children: /* @__PURE__ */ jsxs3("a", { href: csv.href, download: true, rel: "noopener noreferrer", onClick: csv.onSelect, children: [
404
+ /* @__PURE__ */ jsx5(IconFileSpreadsheet, {}),
405
+ csv.label ?? DEFAULT_CSV_LABEL
406
+ ] }) })
407
+ ) : null,
408
+ showPng ? /* @__PURE__ */ jsxs3(DropdownMenuItem, { onSelect: handlePngSelect, disabled: png.disabled ?? false, children: [
409
+ /* @__PURE__ */ jsx5(IconPhoto, {}),
410
+ png.label ?? DEFAULT_PNG_LABEL
411
+ ] }) : null
412
+ ] })
413
+ ] });
414
+ }
415
+
179
416
  // src/components/artifacts/chart-artifact/chart-artifact.tsx
180
- import { useEffect as useEffect2, useMemo as useMemo2 } from "react";
417
+ import { useEffect as useEffect3, useMemo as useMemo2, useRef as useRef2 } from "react";
181
418
 
182
419
  // src/components/ai-elements/artifact.tsx
183
- import { jsx as jsx3 } from "react/jsx-runtime";
184
- var Artifact = ({ className, ...props }) => /* @__PURE__ */ jsx3(
420
+ import { forwardRef as forwardRef4 } from "react";
421
+ import { jsx as jsx6 } from "react/jsx-runtime";
422
+ var Artifact = forwardRef4(({ className, ...props }, ref) => /* @__PURE__ */ jsx6(
185
423
  "div",
186
424
  {
425
+ ref,
187
426
  className: cn("flex flex-col overflow-hidden rounded-lg border bg-background shadow-sm", className),
188
427
  ...props
189
428
  }
190
- );
191
- var ArtifactHeader = ({ className, ...props }) => /* @__PURE__ */ jsx3("div", { className: cn("flex items-center justify-between border-b bg-muted/50 px-4 py-3", className), ...props });
192
- var ArtifactTitle = ({ className, ...props }) => /* @__PURE__ */ jsx3("p", { className: cn("font-medium text-foreground text-sm", className), ...props });
193
- var ArtifactActions = ({ className, ...props }) => /* @__PURE__ */ jsx3("div", { className: cn("flex items-center gap-1", className), ...props });
194
- var ArtifactContent = ({ className, ...props }) => /* @__PURE__ */ jsx3("div", { className: cn("flex-1 overflow-auto p-4", className), ...props });
429
+ ));
430
+ Artifact.displayName = "Artifact";
431
+ var ArtifactHeader = ({ className, ...props }) => /* @__PURE__ */ jsx6("div", { className: cn("flex items-center justify-between border-b bg-muted/50 px-4 py-3", className), ...props });
432
+ var ArtifactTitle = ({ className, ...props }) => /* @__PURE__ */ jsx6("p", { className: cn("font-medium text-foreground text-sm", className), ...props });
433
+ var ArtifactActions = ({ className, ...props }) => /* @__PURE__ */ jsx6("div", { className: cn("flex items-center gap-1", className), ...props });
434
+ var ArtifactContent = ({ className, ...props }) => /* @__PURE__ */ jsx6("div", { className: cn("flex-1 overflow-auto p-4", className), ...props });
195
435
 
196
436
  // src/components/artifacts/status-message.tsx
197
437
  import { createElement as createElement2 } from "react";
@@ -209,9 +449,9 @@ function StatusMessage({ $tone, children }) {
209
449
  var VARIANT_CHART_HEIGHT = "100%";
210
450
 
211
451
  // src/components/artifacts/chart-artifact/chart-variant-services/bar-chart-variant-component.tsx
212
- import { jsx as jsx4 } from "react/jsx-runtime";
452
+ import { jsx as jsx7 } from "react/jsx-runtime";
213
453
  function BarChartVariantComponent({ config, rows }) {
214
- return /* @__PURE__ */ jsx4(
454
+ return /* @__PURE__ */ jsx7(
215
455
  BarChart,
216
456
  {
217
457
  data: rows,
@@ -258,9 +498,9 @@ var BarChartVariantService = {
258
498
  };
259
499
 
260
500
  // src/components/artifacts/chart-artifact/chart-variant-services/line-chart-variant-component.tsx
261
- import { jsx as jsx5 } from "react/jsx-runtime";
501
+ import { jsx as jsx8 } from "react/jsx-runtime";
262
502
  function LineChartVariantComponent({ config, rows }) {
263
- return /* @__PURE__ */ jsx5(LineChart, { data: rows, xKey: config.xKey, series: config.series, height: VARIANT_CHART_HEIGHT });
503
+ return /* @__PURE__ */ jsx8(LineChart, { data: rows, xKey: config.xKey, series: config.series, height: VARIANT_CHART_HEIGHT });
264
504
  }
265
505
 
266
506
  // src/components/artifacts/chart-artifact/chart-variant-services/line-chart-variant-service.tsx
@@ -281,13 +521,13 @@ var LineChartVariantService = {
281
521
  };
282
522
 
283
523
  // src/components/artifacts/chart-artifact/chart-variant-services/pie-chart-variant-component.tsx
284
- import { jsx as jsx6 } from "react/jsx-runtime";
524
+ import { jsx as jsx9 } from "react/jsx-runtime";
285
525
  function PieChartVariantComponent({
286
526
  config,
287
527
  rows,
288
528
  labels
289
529
  }) {
290
- return /* @__PURE__ */ jsx6(
530
+ return /* @__PURE__ */ jsx9(
291
531
  PieChart,
292
532
  {
293
533
  data: rows,
@@ -324,14 +564,14 @@ var ChartVariantServices = {
324
564
  };
325
565
 
326
566
  // src/components/artifacts/chart-artifact/chart-artifact-content.tsx
327
- import { jsx as jsx7 } from "react/jsx-runtime";
567
+ import { jsx as jsx10 } from "react/jsx-runtime";
328
568
  function ChartArtifactContent({ config, rows, labels }) {
329
569
  const service = ChartVariantServices[config.variant];
330
570
  if (!service) {
331
- return /* @__PURE__ */ jsx7(StatusMessage, { $tone: "info", children: labels.unavailable });
571
+ return /* @__PURE__ */ jsx10(StatusMessage, { $tone: "info", children: labels.unavailable });
332
572
  }
333
573
  const Component = service.Component;
334
- return /* @__PURE__ */ jsx7(Component, { config, rows, labels });
574
+ return /* @__PURE__ */ jsx10(Component, { config, rows, labels });
335
575
  }
336
576
 
337
577
  // src/components/artifacts/parse-csv.ts
@@ -356,7 +596,7 @@ function parseCsv(text, columns) {
356
596
  }
357
597
 
358
598
  // src/components/artifacts/use-rows-artifact-data.ts
359
- import { useEffect, useMemo, useState } from "react";
599
+ import { useEffect as useEffect2, useMemo, useState as useState2 } from "react";
360
600
  var LOADING = { status: "loading", rows: null, error: null };
361
601
  var UNAVAILABLE = { status: "unavailable", rows: null, error: null };
362
602
  var INITIAL_INTERNAL = { cacheKey: null, data: LOADING };
@@ -371,8 +611,8 @@ function useRowsArtifactData(presignedUrl, columns, parse) {
371
611
  const colsKey = useMemo(() => columnsFingerprint(columns), [columns]);
372
612
  const safeUrl = presignedUrl && isSafeDownloadUrl(presignedUrl) ? presignedUrl : null;
373
613
  const cacheKey = safeUrl ? `${safeUrl}::${colsKey}` : null;
374
- const [state, setState] = useState(INITIAL_INTERNAL);
375
- useEffect(() => {
614
+ const [state, setState] = useState2(INITIAL_INTERNAL);
615
+ useEffect2(() => {
376
616
  if (!safeUrl) return;
377
617
  const controller = new AbortController();
378
618
  fetch(safeUrl, {
@@ -429,32 +669,71 @@ function isValidChartConfig(config) {
429
669
  }
430
670
 
431
671
  // src/components/artifacts/chart-artifact/chart-artifact.tsx
432
- import { jsx as jsx8, jsxs as jsxs2 } from "react/jsx-runtime";
672
+ import { jsx as jsx11, jsxs as jsxs4 } from "react/jsx-runtime";
433
673
  var TITLE_CLASSES = "text-sm font-semibold text-foreground";
434
674
  var CHART_HEIGHT = 320;
435
675
  function ChartArtifact({ artifact, labels, callbacks }) {
436
676
  const validConfig = isValidChartConfig(artifact.config);
437
- useEffect2(() => {
677
+ const cardRef = useRef2(null);
678
+ useEffect3(() => {
438
679
  if (validConfig) return;
439
680
  console.warn("[clay] ChartArtifact: malformed `config` \u2014 skipping render.");
440
681
  }, [validConfig]);
441
682
  const presignedUrl = artifact.protectedAsset?.presignedUrl ?? null;
442
683
  const resolvedLabels = useMemo2(
443
- () => ({ ...DEFAULT_CHART_ARTIFACT_LABELS, ...labels?.[artifactTypes.CHART] ?? {} }),
684
+ () => ({ ...DEFAULT_CHART_ARTIFACT_LABELS, ...labels ?? {} }),
444
685
  [labels]
445
686
  );
446
687
  const data = useChartArtifactData(
447
688
  validConfig ? presignedUrl : null,
448
689
  validConfig ? artifact.config : { variant: "LINE", series: [], xKey: "x" }
449
690
  );
450
- const onDownload = callbacks?.[artifactTypes.CHART]?.onDownload;
451
- const handleDownloadClick = onDownload ? () => onDownload(artifact) : void 0;
452
- return /* @__PURE__ */ jsxs2(Artifact, { className: "shadow-none w-full rounded-2xl bg-background/70", children: [
453
- /* @__PURE__ */ jsxs2(ArtifactHeader, { className: "bg-background/70", children: [
454
- artifact.title ? /* @__PURE__ */ jsx8(ArtifactTitle, { className: TITLE_CLASSES, children: artifact.title }) : /* @__PURE__ */ jsx8("span", {}),
455
- presignedUrl ? /* @__PURE__ */ jsx8(ArtifactActions, { children: /* @__PURE__ */ jsx8(ArtifactDownloadButton, { href: presignedUrl, label: resolvedLabels.download, onClick: handleDownloadClick }) }) : null
691
+ const { onDownload, onDownloadPng, onCopyAsImage } = callbacks ?? {};
692
+ const handleDownloadCsvSelect = onDownload ? () => onDownload(artifact) : void 0;
693
+ const handleDownloadPngSelect = onDownloadPng ? () => onDownloadPng(artifact) : void 0;
694
+ const handleCopyAsImageClick = onCopyAsImage ? () => onCopyAsImage(artifact) : void 0;
695
+ const canCopyAsImage = data.status === "ready" && data.rows.length > 0;
696
+ const canDownloadPng = data.status === "ready" && data.rows.length > 0;
697
+ const isCsvDownloadAvailable = presignedUrl !== null && isSafeDownloadUrl(presignedUrl);
698
+ const showDownloadMenu = validConfig && canDownloadPng || isCsvDownloadAvailable;
699
+ const showActions = validConfig || showDownloadMenu;
700
+ const pngFilename = (artifact.title ?? "").toLowerCase().split(/[^a-z0-9]+/).filter(Boolean).join("-") || "chart";
701
+ return /* @__PURE__ */ jsxs4(Artifact, { ref: cardRef, className: "shadow-none w-full rounded-2xl bg-background/70", children: [
702
+ /* @__PURE__ */ jsxs4(ArtifactHeader, { className: "bg-background/70", children: [
703
+ artifact.title ? /* @__PURE__ */ jsx11(ArtifactTitle, { className: TITLE_CLASSES, children: artifact.title }) : /* @__PURE__ */ jsx11("span", {}),
704
+ showActions ? /* @__PURE__ */ jsxs4(ArtifactActions, { ...{ [CAPTURE_EXCLUDE_ATTRIBUTE]: "" }, children: [
705
+ validConfig ? /* @__PURE__ */ jsx11(
706
+ ArtifactCopyAsImageButton,
707
+ {
708
+ targetRef: cardRef,
709
+ label: resolvedLabels.copyAsImage,
710
+ disabled: !canCopyAsImage,
711
+ onClick: handleCopyAsImageClick
712
+ }
713
+ ) : null,
714
+ showDownloadMenu ? /* @__PURE__ */ jsx11(
715
+ ArtifactDownloadMenu,
716
+ {
717
+ triggerLabel: resolvedLabels.download,
718
+ actions: {
719
+ csv: isCsvDownloadAvailable ? {
720
+ href: presignedUrl ?? "",
721
+ label: resolvedLabels.downloadCsv,
722
+ onSelect: handleDownloadCsvSelect
723
+ } : void 0,
724
+ png: validConfig ? {
725
+ targetRef: cardRef,
726
+ filename: pngFilename,
727
+ label: resolvedLabels.downloadPng,
728
+ disabled: !canDownloadPng,
729
+ onSelect: handleDownloadPngSelect
730
+ } : void 0
731
+ }
732
+ }
733
+ ) : null
734
+ ] }) : null
456
735
  ] }),
457
- /* @__PURE__ */ jsx8(
736
+ /* @__PURE__ */ jsx11(
458
737
  ArtifactContent,
459
738
  {
460
739
  className: "p-4 overflow-visible flex flex-none items-center justify-center",
@@ -466,25 +745,25 @@ function ChartArtifact({ artifact, labels, callbacks }) {
466
745
  }
467
746
  function renderBody(validConfig, artifact, data, labels) {
468
747
  if (!validConfig) {
469
- return /* @__PURE__ */ jsx8(StatusMessage, { $tone: "info", children: labels.unavailable });
748
+ return /* @__PURE__ */ jsx11(StatusMessage, { $tone: "info", children: labels.unavailable });
470
749
  }
471
750
  if (data.status === "error") {
472
- return /* @__PURE__ */ jsx8(StatusMessage, { $tone: "error", children: labels.loadError });
751
+ return /* @__PURE__ */ jsx11(StatusMessage, { $tone: "error", children: labels.loadError });
473
752
  }
474
753
  if (data.status === "unavailable") {
475
- return /* @__PURE__ */ jsx8(StatusMessage, { $tone: "info", children: labels.unavailable });
754
+ return /* @__PURE__ */ jsx11(StatusMessage, { $tone: "info", children: labels.unavailable });
476
755
  }
477
756
  if (data.status === "loading") {
478
- return /* @__PURE__ */ jsx8(StatusMessage, { $tone: "info", children: labels.loading });
757
+ return /* @__PURE__ */ jsx11(StatusMessage, { $tone: "info", children: labels.loading });
479
758
  }
480
759
  if (data.rows.length === 0) {
481
- return /* @__PURE__ */ jsx8(StatusMessage, { $tone: "info", children: labels.empty });
760
+ return /* @__PURE__ */ jsx11(StatusMessage, { $tone: "info", children: labels.empty });
482
761
  }
483
- return /* @__PURE__ */ jsx8(ChartArtifactContent, { config: artifact.config, rows: data.rows, labels });
762
+ return /* @__PURE__ */ jsx11(ChartArtifactContent, { config: artifact.config, rows: data.rows, labels });
484
763
  }
485
764
 
486
765
  // src/components/artifacts/table-artifact/table-artifact.tsx
487
- import { useEffect as useEffect3, useMemo as useMemo3 } from "react";
766
+ import { useEffect as useEffect4, useMemo as useMemo3 } from "react";
488
767
 
489
768
  // src/components/artifacts/table-artifact/table-artifact-content.constants.ts
490
769
  var TABLE_STATUS = {
@@ -505,7 +784,7 @@ var DATA_ROW_CLASSES = `${ROW_CLASSES} animate-in fade-in duration-300`;
505
784
  var SCROLL_SHADOW = "0 1px 2px rgb(0 0 0 / 0.01), 0 6px 12px -2px rgb(0 0 0 / 0.03)";
506
785
 
507
786
  // src/components/artifacts/table-artifact/table-artifact-content.tsx
508
- import { jsx as jsx9, jsxs as jsxs3 } from "react/jsx-runtime";
787
+ import { jsx as jsx12, jsxs as jsxs5 } from "react/jsx-runtime";
509
788
  function TableArtifactContent({
510
789
  columns,
511
790
  data,
@@ -515,43 +794,43 @@ function TableArtifactContent({
515
794
  labels
516
795
  }) {
517
796
  if (data.status === TABLE_STATUS.ERROR) {
518
- return /* @__PURE__ */ jsx9(StatusMessage, { $tone: "error", children: labels.loadError });
797
+ return /* @__PURE__ */ jsx12(StatusMessage, { $tone: "error", children: labels.loadError });
519
798
  }
520
799
  if (data.status === TABLE_STATUS.UNAVAILABLE) {
521
- return /* @__PURE__ */ jsx9(StatusMessage, { $tone: "info", children: labels.unavailable });
800
+ return /* @__PURE__ */ jsx12(StatusMessage, { $tone: "info", children: labels.unavailable });
522
801
  }
523
802
  if (data.status === TABLE_STATUS.LOADING) {
524
803
  if (typeof knownRowCount === "number" && knownRowCount > 0) {
525
804
  const shimmerCount = Math.min(knownRowCount, maxShimmerRows);
526
- return /* @__PURE__ */ jsx9(ArtifactTable, { columns, maxBodyHeight, children: renderShimmerRows(columns, shimmerCount) });
805
+ return /* @__PURE__ */ jsx12(ArtifactTable, { columns, maxBodyHeight, children: renderShimmerRows(columns, shimmerCount) });
527
806
  }
528
- return /* @__PURE__ */ jsx9(StatusMessage, { $tone: "info", children: labels.loading });
807
+ return /* @__PURE__ */ jsx12(StatusMessage, { $tone: "info", children: labels.loading });
529
808
  }
530
- return /* @__PURE__ */ jsx9(ArtifactTable, { columns, maxBodyHeight, children: data.rows.length === 0 ? renderEmptyRow(columns.length, labels.empty) : renderDataRows(columns, data.rows) });
809
+ return /* @__PURE__ */ jsx12(ArtifactTable, { columns, maxBodyHeight, children: data.rows.length === 0 ? renderEmptyRow(columns.length, labels.empty) : renderDataRows(columns, data.rows) });
531
810
  }
532
811
  function ArtifactTable({ columns, maxBodyHeight, children }) {
533
812
  const { targetRef, isScrolled } = useScrollShadow();
534
- return /* @__PURE__ */ jsx9("div", { ref: targetRef, className: "overscroll-none", style: { maxHeight: maxBodyHeight, overflowY: "auto" }, children: /* @__PURE__ */ jsxs3(Table, { containerClassName: "overflow-visible", children: [
535
- /* @__PURE__ */ jsx9(
813
+ return /* @__PURE__ */ jsx12("div", { ref: targetRef, className: "overscroll-none", style: { maxHeight: maxBodyHeight, overflowY: "auto" }, children: /* @__PURE__ */ jsxs5(Table, { containerClassName: "overflow-visible", children: [
814
+ /* @__PURE__ */ jsx12(
536
815
  TableHeader,
537
816
  {
538
817
  "data-scrolled": isScrolled ? "true" : void 0,
539
818
  className: "sticky top-0 z-10 bg-background/70 backdrop-blur-lg transition-shadow",
540
819
  style: isScrolled ? { boxShadow: SCROLL_SHADOW } : void 0,
541
- children: /* @__PURE__ */ jsx9(TableRow, { className: "hover:bg-transparent", children: columns.map((column) => /* @__PURE__ */ jsx9(TableHead, { scope: "col", className: HEAD_CLASS, children: column.label }, column.key)) })
820
+ children: /* @__PURE__ */ jsx12(TableRow, { className: "hover:bg-transparent", children: columns.map((column) => /* @__PURE__ */ jsx12(TableHead, { scope: "col", className: HEAD_CLASS, children: column.label }, column.key)) })
542
821
  }
543
822
  ),
544
- /* @__PURE__ */ jsx9(TableBody, { children })
823
+ /* @__PURE__ */ jsx12(TableBody, { children })
545
824
  ] }) });
546
825
  }
547
826
  function renderShimmerRows(columns, count) {
548
- return Array.from({ length: count }).map((_, index) => /* @__PURE__ */ jsx9(TableRow, { className: ROW_CLASSES, "aria-hidden": "true", children: columns.map((column) => /* @__PURE__ */ jsx9(TableCell, { className: CELL_CLASS, children: /* @__PURE__ */ jsx9("span", { className: "block h-3 w-3/4 animate-pulse rounded bg-muted-foreground/20" }) }, column.key)) }, `shimmer-${index}`));
827
+ return Array.from({ length: count }).map((_, index) => /* @__PURE__ */ jsx12(TableRow, { className: ROW_CLASSES, "aria-hidden": "true", children: columns.map((column) => /* @__PURE__ */ jsx12(TableCell, { className: CELL_CLASS, children: /* @__PURE__ */ jsx12("span", { className: "block h-3 w-3/4 animate-pulse rounded bg-muted-foreground/20" }) }, column.key)) }, `shimmer-${index}`));
549
828
  }
550
829
  function renderDataRows(columns, rows) {
551
- return rows.map((row, index) => /* @__PURE__ */ jsx9(TableRow, { className: DATA_ROW_CLASSES, children: columns.map((column) => /* @__PURE__ */ jsx9(TableCell, { className: CELL_CLASS, children: formatCell(row, column) }, column.key)) }, index));
830
+ return rows.map((row, index) => /* @__PURE__ */ jsx12(TableRow, { className: DATA_ROW_CLASSES, children: columns.map((column) => /* @__PURE__ */ jsx12(TableCell, { className: CELL_CLASS, children: formatCell(row, column) }, column.key)) }, index));
552
831
  }
553
832
  function renderEmptyRow(colSpan, emptyMessage) {
554
- return /* @__PURE__ */ jsx9(TableRow, { className: "hover:bg-transparent", children: /* @__PURE__ */ jsx9(TableCell, { colSpan, className: "py-6 text-center text-sm text-muted-foreground", children: emptyMessage }) });
833
+ return /* @__PURE__ */ jsx12(TableRow, { className: "hover:bg-transparent", children: /* @__PURE__ */ jsx12(TableCell, { colSpan, className: "py-6 text-center text-sm text-muted-foreground", children: emptyMessage }) });
555
834
  }
556
835
 
557
836
  // src/components/artifacts/table-artifact/use-table-artifact-data.ts
@@ -576,19 +855,19 @@ function isValidTableColumn(column) {
576
855
  }
577
856
 
578
857
  // src/components/artifacts/table-artifact/table-artifact.tsx
579
- import { jsx as jsx10, jsxs as jsxs4 } from "react/jsx-runtime";
858
+ import { jsx as jsx13, jsxs as jsxs6 } from "react/jsx-runtime";
580
859
  var TITLE_CLASSES2 = "text-sm font-semibold text-foreground";
581
860
  var TABLE_MAX_BODY_HEIGHT = 480;
582
861
  var MAX_SHIMMER_ROWS = 10;
583
862
  function TableArtifact({ artifact, labels, callbacks }) {
584
863
  const validConfig = isValidTableConfig(artifact.config);
585
864
  const validColumns = validConfig ? artifact.config.columns : null;
586
- useEffect3(() => {
865
+ useEffect4(() => {
587
866
  if (validConfig) return;
588
867
  console.warn("[clay] TableArtifact: malformed `config` \u2014 skipping render.");
589
868
  }, [validConfig]);
590
869
  const resolvedLabels = useMemo3(
591
- () => ({ ...DEFAULT_TABLE_ARTIFACT_LABELS, ...labels?.[artifactTypes.TABLE] ?? {} }),
870
+ () => ({ ...DEFAULT_TABLE_ARTIFACT_LABELS, ...labels ?? {} }),
592
871
  [labels]
593
872
  );
594
873
  const data = useTableArtifactData(
@@ -596,13 +875,13 @@ function TableArtifact({ artifact, labels, callbacks }) {
596
875
  validColumns ?? []
597
876
  );
598
877
  const presignedUrl = artifact.protectedAsset?.presignedUrl ?? null;
599
- const onDownload = callbacks?.[artifactTypes.TABLE]?.onDownload;
878
+ const onDownload = callbacks?.onDownload;
600
879
  const handleDownloadClick = onDownload ? () => onDownload(artifact) : void 0;
601
880
  if (!validConfig || !validColumns) {
602
- return /* @__PURE__ */ jsxs4(Artifact, { className: "shadow-none w-full rounded-2xl bg-background/70", children: [
603
- /* @__PURE__ */ jsxs4(ArtifactHeader, { className: "bg-background/70", children: [
604
- artifact.title ? /* @__PURE__ */ jsx10(ArtifactTitle, { className: TITLE_CLASSES2, children: artifact.title }) : /* @__PURE__ */ jsx10("span", {}),
605
- presignedUrl ? /* @__PURE__ */ jsx10(ArtifactActions, { children: /* @__PURE__ */ jsx10(
881
+ return /* @__PURE__ */ jsxs6(Artifact, { className: "shadow-none w-full rounded-2xl bg-background/70", children: [
882
+ /* @__PURE__ */ jsxs6(ArtifactHeader, { className: "bg-background/70", children: [
883
+ artifact.title ? /* @__PURE__ */ jsx13(ArtifactTitle, { className: TITLE_CLASSES2, children: artifact.title }) : /* @__PURE__ */ jsx13("span", {}),
884
+ presignedUrl ? /* @__PURE__ */ jsx13(ArtifactActions, { children: /* @__PURE__ */ jsx13(
606
885
  ArtifactDownloadButton,
607
886
  {
608
887
  href: presignedUrl,
@@ -611,17 +890,17 @@ function TableArtifact({ artifact, labels, callbacks }) {
611
890
  }
612
891
  ) }) : null
613
892
  ] }),
614
- /* @__PURE__ */ jsx10(ArtifactContent, { className: "p-0 overflow-auto", style: { maxHeight: TABLE_MAX_BODY_HEIGHT }, children: /* @__PURE__ */ jsx10(StatusMessage, { $tone: "info", children: resolvedLabels.unavailable }) })
893
+ /* @__PURE__ */ jsx13(ArtifactContent, { className: "p-0 overflow-auto", style: { maxHeight: TABLE_MAX_BODY_HEIGHT }, children: /* @__PURE__ */ jsx13(StatusMessage, { $tone: "info", children: resolvedLabels.unavailable }) })
615
894
  ] });
616
895
  }
617
896
  return (
618
897
  // The table body owns its own scroll container (`ArtifactTable` inside `TableArtifactContent`).
619
898
  // Disable the outer overflow container so only one scroll context exists — this prevents
620
899
  // double-scroll nesting and lets `useScrollShadow` find the correct scroll ancestor.
621
- /* @__PURE__ */ jsxs4(Artifact, { className: "shadow-none w-full rounded-2xl bg-background/70", children: [
622
- /* @__PURE__ */ jsxs4(ArtifactHeader, { className: "bg-background/70", children: [
623
- artifact.title ? /* @__PURE__ */ jsx10(ArtifactTitle, { className: TITLE_CLASSES2, children: artifact.title }) : /* @__PURE__ */ jsx10("span", {}),
624
- /* @__PURE__ */ jsx10(ArtifactActions, { children: /* @__PURE__ */ jsx10(
900
+ /* @__PURE__ */ jsxs6(Artifact, { className: "shadow-none w-full rounded-2xl bg-background/70", children: [
901
+ /* @__PURE__ */ jsxs6(ArtifactHeader, { className: "bg-background/70", children: [
902
+ artifact.title ? /* @__PURE__ */ jsx13(ArtifactTitle, { className: TITLE_CLASSES2, children: artifact.title }) : /* @__PURE__ */ jsx13("span", {}),
903
+ /* @__PURE__ */ jsx13(ArtifactActions, { children: /* @__PURE__ */ jsx13(
625
904
  ArtifactDownloadButton,
626
905
  {
627
906
  href: presignedUrl ?? "",
@@ -630,7 +909,7 @@ function TableArtifact({ artifact, labels, callbacks }) {
630
909
  }
631
910
  ) })
632
911
  ] }),
633
- /* @__PURE__ */ jsx10(ArtifactContent, { className: "p-0 overflow-visible", style: { maxHeight: "none" }, children: /* @__PURE__ */ jsx10(
912
+ /* @__PURE__ */ jsx13(ArtifactContent, { className: "p-0 overflow-visible", style: { maxHeight: "none" }, children: /* @__PURE__ */ jsx13(
634
913
  TableArtifactContent,
635
914
  {
636
915
  columns: validColumns,
@@ -692,8 +971,10 @@ export {
692
971
  IconCopy,
693
972
  IconThumbDown,
694
973
  IconThumbUp,
974
+ ArtifactCopyAsImageButton,
695
975
  ArtifactDownloadButton,
696
976
  isSafeDownloadUrl,
977
+ ArtifactDownloadMenu,
697
978
  ChartArtifact,
698
979
  TableArtifact,
699
980
  ArtifactServices,
@@ -703,9 +984,13 @@ export {
703
984
 
704
985
  @tabler/icons-react/dist/esm/defaultAttributes.mjs:
705
986
  @tabler/icons-react/dist/esm/createReactComponent.mjs:
987
+ @tabler/icons-react/dist/esm/icons/IconAlertCircle.mjs:
706
988
  @tabler/icons-react/dist/esm/icons/IconCheck.mjs:
707
989
  @tabler/icons-react/dist/esm/icons/IconCopy.mjs:
708
990
  @tabler/icons-react/dist/esm/icons/IconDownload.mjs:
991
+ @tabler/icons-react/dist/esm/icons/IconFileSpreadsheet.mjs:
992
+ @tabler/icons-react/dist/esm/icons/IconLoader2.mjs:
993
+ @tabler/icons-react/dist/esm/icons/IconPhoto.mjs:
709
994
  @tabler/icons-react/dist/esm/icons/IconThumbDown.mjs:
710
995
  @tabler/icons-react/dist/esm/icons/IconThumbUp.mjs:
711
996
  @tabler/icons-react/dist/esm/tabler-icons-react.mjs:
@@ -716,4 +1001,4 @@ export {
716
1001
  * See the LICENSE file in the root directory of this source tree.
717
1002
  *)
718
1003
  */
719
- //# sourceMappingURL=chunk-Z5JWF24N.js.map
1004
+ //# sourceMappingURL=chunk-DT44KNCV.js.map