@shapesos/clay 0.11.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/README.md +60 -0
  2. package/dist/artifacts.cjs +858 -132
  3. package/dist/artifacts.cjs.map +1 -1
  4. package/dist/artifacts.d.cts +38 -5
  5. package/dist/artifacts.d.ts +38 -5
  6. package/dist/artifacts.js +13 -3
  7. package/dist/blocks.cjs +1185 -467
  8. package/dist/blocks.cjs.map +1 -1
  9. package/dist/blocks.css +1 -1
  10. package/dist/blocks.d.cts +23 -10
  11. package/dist/blocks.d.ts +23 -10
  12. package/dist/blocks.js +6 -3
  13. package/dist/chart.cjs +594 -0
  14. package/dist/chart.cjs.map +1 -0
  15. package/dist/chart.d.cts +439 -0
  16. package/dist/chart.d.ts +439 -0
  17. package/dist/chart.js +32 -0
  18. package/dist/chat.cjs +1207 -489
  19. package/dist/chat.cjs.map +1 -1
  20. package/dist/chat.d.cts +3 -2
  21. package/dist/chat.d.ts +3 -2
  22. package/dist/chat.js +7 -4
  23. package/dist/{chunk-MXOPG747.js → chunk-36CB624W.js} +7 -7
  24. package/dist/chunk-36CB624W.js.map +1 -0
  25. package/dist/{chunk-L35M3OD5.js → chunk-4MZZH3WX.js} +5 -11
  26. package/dist/chunk-4MZZH3WX.js.map +1 -0
  27. package/dist/chunk-AQEJRMRN.js +1 -0
  28. package/dist/chunk-AQEJRMRN.js.map +1 -0
  29. package/dist/{chunk-OUW6PUEB.js → chunk-FFX3CAOX.js} +7 -7
  30. package/dist/chunk-P6GUNIAE.js +11 -0
  31. package/dist/chunk-P6GUNIAE.js.map +1 -0
  32. package/dist/chunk-QXGYMDIA.js +477 -0
  33. package/dist/chunk-QXGYMDIA.js.map +1 -0
  34. package/dist/{chunk-BX5TCEPR.js → chunk-Z5JWF24N.js} +388 -105
  35. package/dist/chunk-Z5JWF24N.js.map +1 -0
  36. package/dist/index.cjs +924 -198
  37. package/dist/index.cjs.map +1 -1
  38. package/dist/index.d.cts +4 -3
  39. package/dist/index.d.ts +4 -3
  40. package/dist/index.js +23 -15
  41. package/dist/table.js +2 -1
  42. package/dist/types-B2aYk82c.d.cts +29 -0
  43. package/dist/types-B2aYk82c.d.ts +29 -0
  44. package/dist/types-C0BvwliI.d.cts +332 -0
  45. package/dist/types-C5bFH4v3.d.ts +332 -0
  46. package/dist/{types-DuuRI4ll.d.cts → types-DCutaXjZ.d.cts} +16 -1
  47. package/dist/{types-C9XX-Uhk.d.ts → types-uPfn67Dc.d.ts} +16 -1
  48. package/package.json +13 -1
  49. package/dist/chunk-BX5TCEPR.js.map +0 -1
  50. package/dist/chunk-L35M3OD5.js.map +0 -1
  51. package/dist/chunk-MEJESPTZ.js +0 -1
  52. package/dist/chunk-MXOPG747.js.map +0 -1
  53. package/dist/types-3Gzk7cRt.d.cts +0 -121
  54. package/dist/types-3Gzk7cRt.d.ts +0 -121
  55. /package/dist/{chunk-MEJESPTZ.js.map → chart.js.map} +0 -0
  56. /package/dist/{chunk-OUW6PUEB.js.map → chunk-FFX3CAOX.js.map} +0 -0
@@ -1,3 +1,8 @@
1
+ import {
2
+ BarChart,
3
+ LineChart,
4
+ PieChart
5
+ } from "./chunk-QXGYMDIA.js";
1
6
  import {
2
7
  Table,
3
8
  TableBody,
@@ -5,16 +10,38 @@ import {
5
10
  TableHead,
6
11
  TableHeader,
7
12
  TableRow,
8
- cn,
9
13
  useScrollShadow
10
- } from "./chunk-L35M3OD5.js";
14
+ } from "./chunk-4MZZH3WX.js";
15
+ import {
16
+ cn
17
+ } from "./chunk-P6GUNIAE.js";
11
18
  import {
12
19
  IconButton
13
20
  } from "./chunk-OBOXCBDL.js";
14
21
 
15
- // src/components/artifacts/types.ts
22
+ // src/components/artifacts/artifact-base.ts
16
23
  var artifactTypes = {
17
- TABLE: "TABLE"
24
+ TABLE: "TABLE",
25
+ CHART: "CHART"
26
+ };
27
+
28
+ // src/components/artifacts/chart-artifact/types.ts
29
+ var DEFAULT_CHART_ARTIFACT_LABELS = {
30
+ download: "Download CSV",
31
+ unavailable: "Chart data is unavailable.",
32
+ loadError: "Couldn't load chart data.",
33
+ loading: "Loading chart\u2026",
34
+ empty: "No data to chart.",
35
+ othersCategoryLabels: ["Others", "Other"]
36
+ };
37
+
38
+ // src/components/artifacts/table-artifact/types.ts
39
+ var DEFAULT_TABLE_ARTIFACT_LABELS = {
40
+ download: "Download CSV",
41
+ unavailable: "Table data is unavailable.",
42
+ loadError: "Couldn't load table data.",
43
+ loading: "Loading table\u2026",
44
+ empty: "No rows to display."
18
45
  };
19
46
 
20
47
  // node_modules/@tabler/icons-react/dist/esm/createReactComponent.mjs
@@ -116,7 +143,12 @@ TooltipContent.displayName = TooltipPrimitive.Content.displayName;
116
143
 
117
144
  // src/components/artifacts/artifact-download-button/artifact-download-button.tsx
118
145
  import { jsx as jsx2, jsxs } from "react/jsx-runtime";
119
- function ArtifactDownloadButton({ href }) {
146
+ var DEFAULT_LABEL = "Download CSV";
147
+ function ArtifactDownloadButton({
148
+ href,
149
+ label = DEFAULT_LABEL,
150
+ onClick
151
+ }) {
120
152
  if (!isSafeDownloadUrl(href)) return null;
121
153
  return /* @__PURE__ */ jsx2(TooltipProvider, { children: /* @__PURE__ */ jsxs(Tooltip, { children: [
122
154
  /* @__PURE__ */ jsx2(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx2(
@@ -126,12 +158,13 @@ function ArtifactDownloadButton({ href }) {
126
158
  href,
127
159
  download: true,
128
160
  rel: "noopener noreferrer",
161
+ onClick,
129
162
  icon: IconDownload,
130
163
  size: "small",
131
- "aria-label": "Download artifact"
164
+ "aria-label": label
132
165
  }
133
166
  ) }),
134
- /* @__PURE__ */ jsx2(TooltipContent, { children: "Download" })
167
+ /* @__PURE__ */ jsx2(TooltipContent, { children: label })
135
168
  ] }) });
136
169
  }
137
170
  function isSafeDownloadUrl(url) {
@@ -143,8 +176,8 @@ function isSafeDownloadUrl(url) {
143
176
  }
144
177
  }
145
178
 
146
- // src/components/artifacts/table-artifact/table-artifact.tsx
147
- import { useEffect as useEffect2 } from "react";
179
+ // src/components/artifacts/chart-artifact/chart-artifact.tsx
180
+ import { useEffect as useEffect2, useMemo as useMemo2 } from "react";
148
181
 
149
182
  // src/components/ai-elements/artifact.tsx
150
183
  import { jsx as jsx3 } from "react/jsx-runtime";
@@ -160,7 +193,7 @@ var ArtifactTitle = ({ className, ...props }) => /* @__PURE__ */ jsx3("p", { cla
160
193
  var ArtifactActions = ({ className, ...props }) => /* @__PURE__ */ jsx3("div", { className: cn("flex items-center gap-1", className), ...props });
161
194
  var ArtifactContent = ({ className, ...props }) => /* @__PURE__ */ jsx3("div", { className: cn("flex-1 overflow-auto p-4", className), ...props });
162
195
 
163
- // src/components/artifacts/table-artifact/table-artifact-styles.ts
196
+ // src/components/artifacts/status-message.tsx
164
197
  import { createElement as createElement2 } from "react";
165
198
  function StatusMessage({ $tone, children }) {
166
199
  return createElement2(
@@ -172,78 +205,136 @@ function StatusMessage({ $tone, children }) {
172
205
  );
173
206
  }
174
207
 
175
- // src/components/artifacts/table-artifact/table-artifact-content.constants.ts
176
- var TABLE_STATUS = {
177
- LOADING: "loading",
178
- READY: "ready",
179
- ERROR: "error",
180
- UNAVAILABLE: "unavailable"
181
- };
208
+ // src/components/artifacts/chart-artifact/chart-variant-services/constants.ts
209
+ var VARIANT_CHART_HEIGHT = "100%";
182
210
 
183
- // src/components/artifacts/table-artifact/table-artifact-content.utils.ts
184
- function formatCell(row, column) {
185
- return row[column.key] ?? "";
211
+ // src/components/artifacts/chart-artifact/chart-variant-services/bar-chart-variant-component.tsx
212
+ import { jsx as jsx4 } from "react/jsx-runtime";
213
+ function BarChartVariantComponent({ config, rows }) {
214
+ return /* @__PURE__ */ jsx4(
215
+ BarChart,
216
+ {
217
+ data: rows,
218
+ xKey: config.xKey,
219
+ series: config.series,
220
+ stacked: config.stacked,
221
+ height: VARIANT_CHART_HEIGHT
222
+ }
223
+ );
186
224
  }
187
- var HEAD_CLASS = "h-auto py-2 px-4 text-xs font-medium text-foreground whitespace-nowrap text-left";
188
- var CELL_CLASS = "p-4 text-sm font-normal text-foreground align-top min-w-[150px] text-left";
189
- var ROW_CLASSES = "border-b-brown-30 bg-background/70 hover:bg-secondary";
190
- var DATA_ROW_CLASSES = `${ROW_CLASSES} animate-in fade-in duration-300`;
191
- var SCROLL_SHADOW = "0 1px 2px rgb(0 0 0 / 0.01), 0 6px 12px -2px rgb(0 0 0 / 0.03)";
192
225
 
193
- // src/components/artifacts/table-artifact/table-artifact-content.tsx
194
- import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
195
- function TableArtifactContent({
196
- columns,
197
- data,
198
- knownRowCount,
199
- maxShimmerRows,
200
- maxBodyHeight,
201
- unavailableMessage
202
- }) {
203
- if (data.status === TABLE_STATUS.ERROR) {
204
- return /* @__PURE__ */ jsx4(StatusMessage, { $tone: "error", children: "Couldn't load table data." });
205
- }
206
- if (data.status === TABLE_STATUS.UNAVAILABLE) {
207
- return /* @__PURE__ */ jsx4(StatusMessage, { $tone: "info", children: unavailableMessage });
208
- }
209
- if (data.status === TABLE_STATUS.LOADING) {
210
- if (typeof knownRowCount === "number" && knownRowCount > 0) {
211
- const shimmerCount = Math.min(knownRowCount, maxShimmerRows);
212
- return /* @__PURE__ */ jsx4(ArtifactTable, { columns, maxBodyHeight, children: renderShimmerRows(columns, shimmerCount) });
213
- }
214
- return /* @__PURE__ */ jsx4(StatusMessage, { $tone: "info", children: "Loading table\u2026" });
215
- }
216
- return /* @__PURE__ */ jsx4(ArtifactTable, { columns, maxBodyHeight, children: data.rows.length === 0 ? renderEmptyRow(columns.length) : renderDataRows(columns, data.rows) });
226
+ // src/components/artifacts/chart-artifact/chart-variant-services/shared-column-keys.ts
227
+ function seriesBasedColumnKeys(config) {
228
+ return [config.xKey, ...config.series.map((s) => s.key)];
217
229
  }
218
- function ArtifactTable({ columns, maxBodyHeight, children }) {
219
- const { targetRef, isScrolled } = useScrollShadow();
220
- return /* @__PURE__ */ jsx4("div", { ref: targetRef, className: "overscroll-none", style: { maxHeight: maxBodyHeight, overflowY: "auto" }, children: /* @__PURE__ */ jsxs2(Table, { containerClassName: "overflow-visible", children: [
221
- /* @__PURE__ */ jsx4(
222
- TableHeader,
223
- {
224
- "data-scrolled": isScrolled ? "true" : void 0,
225
- className: "sticky top-0 z-10 bg-background/70 backdrop-blur-lg transition-shadow",
226
- style: isScrolled ? { boxShadow: SCROLL_SHADOW } : void 0,
227
- children: /* @__PURE__ */ jsx4(TableRow, { className: "hover:bg-transparent", children: columns.map((column) => /* @__PURE__ */ jsx4(TableHead, { scope: "col", className: HEAD_CLASS, children: column.label }, column.key)) })
228
- }
229
- ),
230
- /* @__PURE__ */ jsx4(TableBody, { children })
231
- ] }) });
230
+ function pieColumnKeys(config) {
231
+ return [config.categoryKey, config.valueKey];
232
232
  }
233
- function renderShimmerRows(columns, count) {
234
- return Array.from({ length: count }).map((_, index) => /* @__PURE__ */ jsx4(TableRow, { className: ROW_CLASSES, "aria-hidden": "true", children: columns.map((column) => /* @__PURE__ */ jsx4(TableCell, { className: CELL_CLASS, children: /* @__PURE__ */ jsx4("span", { className: "block h-3 w-3/4 animate-pulse rounded bg-muted-foreground/20" }) }, column.key)) }, `shimmer-${index}`));
233
+
234
+ // src/components/artifacts/chart-artifact/chart-variant-services/shared-validators.ts
235
+ function isValidChartSeries(series) {
236
+ if (!series || typeof series !== "object") return false;
237
+ const candidate = series;
238
+ if (typeof candidate.key !== "string" || candidate.key.length === 0) return false;
239
+ if (typeof candidate.label !== "string") return false;
240
+ return true;
235
241
  }
236
- function renderDataRows(columns, rows) {
237
- return rows.map((row, index) => /* @__PURE__ */ jsx4(TableRow, { className: DATA_ROW_CLASSES, children: columns.map((column) => /* @__PURE__ */ jsx4(TableCell, { className: CELL_CLASS, children: formatCell(row, column) }, column.key)) }, index));
242
+
243
+ // src/components/artifacts/chart-artifact/chart-variant-services/bar-chart-variant-service.tsx
244
+ function isValidBarChartConfig(config) {
245
+ if (!config || typeof config !== "object") return false;
246
+ const candidate = config;
247
+ if (candidate.variant !== "BAR") return false;
248
+ if (typeof candidate.xKey !== "string" || candidate.xKey.length === 0) return false;
249
+ if (!Array.isArray(candidate.series) || candidate.series.length === 0) return false;
250
+ if (!candidate.series.every(isValidChartSeries)) return false;
251
+ return true;
238
252
  }
239
- function renderEmptyRow(colSpan) {
240
- return /* @__PURE__ */ jsx4(TableRow, { className: "hover:bg-transparent", children: /* @__PURE__ */ jsx4(TableCell, { colSpan, className: "py-6 text-center text-sm text-muted-foreground", children: "No rows to display." }) });
253
+ var BarChartVariantService = {
254
+ variant: "BAR",
255
+ isValidConfig: isValidBarChartConfig,
256
+ columnKeys: seriesBasedColumnKeys,
257
+ Component: BarChartVariantComponent
258
+ };
259
+
260
+ // src/components/artifacts/chart-artifact/chart-variant-services/line-chart-variant-component.tsx
261
+ import { jsx as jsx5 } from "react/jsx-runtime";
262
+ function LineChartVariantComponent({ config, rows }) {
263
+ return /* @__PURE__ */ jsx5(LineChart, { data: rows, xKey: config.xKey, series: config.series, height: VARIANT_CHART_HEIGHT });
241
264
  }
242
265
 
243
- // src/components/artifacts/table-artifact/use-table-artifact-data.ts
244
- import { useEffect, useMemo, useState } from "react";
266
+ // src/components/artifacts/chart-artifact/chart-variant-services/line-chart-variant-service.tsx
267
+ function isValidLineChartConfig(config) {
268
+ if (!config || typeof config !== "object") return false;
269
+ const candidate = config;
270
+ if (candidate.variant !== "LINE") return false;
271
+ if (typeof candidate.xKey !== "string" || candidate.xKey.length === 0) return false;
272
+ if (!Array.isArray(candidate.series) || candidate.series.length === 0) return false;
273
+ if (!candidate.series.every(isValidChartSeries)) return false;
274
+ return true;
275
+ }
276
+ var LineChartVariantService = {
277
+ variant: "LINE",
278
+ isValidConfig: isValidLineChartConfig,
279
+ columnKeys: seriesBasedColumnKeys,
280
+ Component: LineChartVariantComponent
281
+ };
282
+
283
+ // src/components/artifacts/chart-artifact/chart-variant-services/pie-chart-variant-component.tsx
284
+ import { jsx as jsx6 } from "react/jsx-runtime";
285
+ function PieChartVariantComponent({
286
+ config,
287
+ rows,
288
+ labels
289
+ }) {
290
+ return /* @__PURE__ */ jsx6(
291
+ PieChart,
292
+ {
293
+ data: rows,
294
+ categoryKey: config.categoryKey,
295
+ valueKey: config.valueKey,
296
+ othersCategoryLabels: labels.othersCategoryLabels,
297
+ height: VARIANT_CHART_HEIGHT
298
+ }
299
+ );
300
+ }
245
301
 
246
- // src/components/artifacts/table-artifact/parse-csv.ts
302
+ // src/components/artifacts/chart-artifact/chart-variant-services/pie-chart-variant-service.tsx
303
+ function isValidPieChartConfig(config) {
304
+ if (!config || typeof config !== "object") return false;
305
+ const candidate = config;
306
+ if (candidate.variant !== "PIE") return false;
307
+ if (typeof candidate.categoryKey !== "string" || candidate.categoryKey.length === 0) return false;
308
+ if (typeof candidate.valueKey !== "string" || candidate.valueKey.length === 0) return false;
309
+ if (!Array.isArray(candidate.series)) return false;
310
+ return true;
311
+ }
312
+ var PieChartVariantService = {
313
+ variant: "PIE",
314
+ isValidConfig: isValidPieChartConfig,
315
+ columnKeys: pieColumnKeys,
316
+ Component: PieChartVariantComponent
317
+ };
318
+
319
+ // src/components/artifacts/chart-artifact/chart-variant-services/index.ts
320
+ var ChartVariantServices = {
321
+ LINE: LineChartVariantService,
322
+ BAR: BarChartVariantService,
323
+ PIE: PieChartVariantService
324
+ };
325
+
326
+ // src/components/artifacts/chart-artifact/chart-artifact-content.tsx
327
+ import { jsx as jsx7 } from "react/jsx-runtime";
328
+ function ChartArtifactContent({ config, rows, labels }) {
329
+ const service = ChartVariantServices[config.variant];
330
+ if (!service) {
331
+ return /* @__PURE__ */ jsx7(StatusMessage, { $tone: "info", children: labels.unavailable });
332
+ }
333
+ const Component = service.Component;
334
+ return /* @__PURE__ */ jsx7(Component, { config, rows, labels });
335
+ }
336
+
337
+ // src/components/artifacts/parse-csv.ts
247
338
  import Papa from "papaparse";
248
339
  function parseCsv(text, columns) {
249
340
  const result = Papa.parse(text, {
@@ -253,6 +344,7 @@ function parseCsv(text, columns) {
253
344
  });
254
345
  const csvHeaders = new Set(result.meta?.fields ?? []);
255
346
  const presentColumns = columns.filter((col) => csvHeaders.has(col.key));
347
+ if (presentColumns.length === 0) return [];
256
348
  return result.data.map((rawRow) => {
257
349
  const row = {};
258
350
  for (const column of presentColumns) {
@@ -263,21 +355,27 @@ function parseCsv(text, columns) {
263
355
  });
264
356
  }
265
357
 
266
- // src/components/artifacts/table-artifact/use-table-artifact-data.ts
358
+ // src/components/artifacts/use-rows-artifact-data.ts
359
+ import { useEffect, useMemo, useState } from "react";
267
360
  var LOADING = { status: "loading", rows: null, error: null };
268
361
  var UNAVAILABLE = { status: "unavailable", rows: null, error: null };
269
362
  var INITIAL_INTERNAL = { cacheKey: null, data: LOADING };
270
363
  function columnsFingerprint(columns) {
271
- return columns.map((c) => `${c.key}:${c.type ?? "string"}`).join("|");
364
+ return columns.map((c) => c.key).join("|");
272
365
  }
273
- function useTableArtifactData(presignedUrl, columns) {
366
+ function scrubUrl(url) {
367
+ const q = url.indexOf("?");
368
+ return q === -1 ? url : `${url.slice(0, q)}?<redacted>`;
369
+ }
370
+ function useRowsArtifactData(presignedUrl, columns, parse) {
274
371
  const colsKey = useMemo(() => columnsFingerprint(columns), [columns]);
275
- const cacheKey = presignedUrl ? `${presignedUrl}::${colsKey}` : null;
372
+ const safeUrl = presignedUrl && isSafeDownloadUrl(presignedUrl) ? presignedUrl : null;
373
+ const cacheKey = safeUrl ? `${safeUrl}::${colsKey}` : null;
276
374
  const [state, setState] = useState(INITIAL_INTERNAL);
277
375
  useEffect(() => {
278
- if (!presignedUrl) return;
376
+ if (!safeUrl) return;
279
377
  const controller = new AbortController();
280
- fetch(presignedUrl, {
378
+ fetch(safeUrl, {
281
379
  signal: controller.signal,
282
380
  headers: { Accept: "text/csv" },
283
381
  // Presigned URLs are cross-origin S3 in production and don't need credentials. Explicit
@@ -285,33 +383,182 @@ function useTableArtifactData(presignedUrl, columns) {
285
383
  // session cookies or auth headers.
286
384
  credentials: "omit"
287
385
  }).then(async (response) => {
288
- if (!response.ok) throw new Error(`Failed to load table data (${response.status})`);
386
+ if (!response.ok) throw new Error(`Failed to load artifact data (${response.status})`);
289
387
  return response.text();
290
388
  }).then((text) => {
291
389
  if (controller.signal.aborted) return;
292
390
  try {
293
- const rows = parseCsv(text, columns);
294
- setState({ cacheKey: `${presignedUrl}::${colsKey}`, data: { status: "ready", rows, error: null } });
391
+ const rows = parse(text, columns);
392
+ setState({ cacheKey: `${safeUrl}::${colsKey}`, data: { status: "ready", rows, error: null } });
295
393
  } catch (parseError) {
296
- console.error("[TableArtifact] CSV parse failed", parseError);
297
394
  const err = parseError instanceof Error ? parseError : new Error(String(parseError));
298
- setState({ cacheKey: `${presignedUrl}::${colsKey}`, data: { status: "error", rows: null, error: err } });
395
+ console.error("[ArtifactData] CSV parse failed:", err.message);
396
+ setState({ cacheKey: `${safeUrl}::${colsKey}`, data: { status: "error", rows: null, error: err } });
299
397
  }
300
398
  }).catch((error) => {
301
399
  if (controller.signal.aborted) return;
302
- console.error("[TableArtifact] Fetch failed", error);
303
400
  const err = error instanceof Error ? error : new Error(String(error));
304
- setState({ cacheKey: `${presignedUrl}::${colsKey}`, data: { status: "error", rows: null, error: err } });
401
+ console.error(`[ArtifactData] Fetch failed [${scrubUrl(safeUrl)}]:`, err.message);
402
+ setState({ cacheKey: `${safeUrl}::${colsKey}`, data: { status: "error", rows: null, error: err } });
305
403
  });
306
404
  return () => {
307
405
  controller.abort();
308
406
  };
309
- }, [presignedUrl, colsKey]);
310
- if (!presignedUrl) return UNAVAILABLE;
407
+ }, [safeUrl, colsKey]);
408
+ if (!safeUrl) return UNAVAILABLE;
311
409
  if (state.cacheKey !== cacheKey) return LOADING;
312
410
  return state.data;
313
411
  }
314
412
 
413
+ // src/components/artifacts/chart-artifact/use-chart-artifact-data.ts
414
+ function useChartArtifactData(presignedUrl, config) {
415
+ const service = ChartVariantServices[config.variant];
416
+ const keys = service ? service.columnKeys(config) : [];
417
+ const columns = keys.map((key) => ({ key }));
418
+ return useRowsArtifactData(presignedUrl, columns, parseCsv);
419
+ }
420
+
421
+ // src/components/artifacts/chart-artifact/validate.ts
422
+ function isValidChartConfig(config) {
423
+ if (!config || typeof config !== "object") return false;
424
+ const variant = config.variant;
425
+ if (typeof variant !== "string") return false;
426
+ const service = ChartVariantServices[variant];
427
+ if (!service) return false;
428
+ return service.isValidConfig(config);
429
+ }
430
+
431
+ // src/components/artifacts/chart-artifact/chart-artifact.tsx
432
+ import { jsx as jsx8, jsxs as jsxs2 } from "react/jsx-runtime";
433
+ var TITLE_CLASSES = "text-sm font-semibold text-foreground";
434
+ var CHART_HEIGHT = 320;
435
+ function ChartArtifact({ artifact, labels, callbacks }) {
436
+ const validConfig = isValidChartConfig(artifact.config);
437
+ useEffect2(() => {
438
+ if (validConfig) return;
439
+ console.warn("[clay] ChartArtifact: malformed `config` \u2014 skipping render.");
440
+ }, [validConfig]);
441
+ const presignedUrl = artifact.protectedAsset?.presignedUrl ?? null;
442
+ const resolvedLabels = useMemo2(
443
+ () => ({ ...DEFAULT_CHART_ARTIFACT_LABELS, ...labels?.[artifactTypes.CHART] ?? {} }),
444
+ [labels]
445
+ );
446
+ const data = useChartArtifactData(
447
+ validConfig ? presignedUrl : null,
448
+ validConfig ? artifact.config : { variant: "LINE", series: [], xKey: "x" }
449
+ );
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
456
+ ] }),
457
+ /* @__PURE__ */ jsx8(
458
+ ArtifactContent,
459
+ {
460
+ className: "p-4 overflow-visible flex flex-none items-center justify-center",
461
+ style: { height: CHART_HEIGHT },
462
+ children: renderBody(validConfig, artifact, data, resolvedLabels)
463
+ }
464
+ )
465
+ ] });
466
+ }
467
+ function renderBody(validConfig, artifact, data, labels) {
468
+ if (!validConfig) {
469
+ return /* @__PURE__ */ jsx8(StatusMessage, { $tone: "info", children: labels.unavailable });
470
+ }
471
+ if (data.status === "error") {
472
+ return /* @__PURE__ */ jsx8(StatusMessage, { $tone: "error", children: labels.loadError });
473
+ }
474
+ if (data.status === "unavailable") {
475
+ return /* @__PURE__ */ jsx8(StatusMessage, { $tone: "info", children: labels.unavailable });
476
+ }
477
+ if (data.status === "loading") {
478
+ return /* @__PURE__ */ jsx8(StatusMessage, { $tone: "info", children: labels.loading });
479
+ }
480
+ if (data.rows.length === 0) {
481
+ return /* @__PURE__ */ jsx8(StatusMessage, { $tone: "info", children: labels.empty });
482
+ }
483
+ return /* @__PURE__ */ jsx8(ChartArtifactContent, { config: artifact.config, rows: data.rows, labels });
484
+ }
485
+
486
+ // src/components/artifacts/table-artifact/table-artifact.tsx
487
+ import { useEffect as useEffect3, useMemo as useMemo3 } from "react";
488
+
489
+ // src/components/artifacts/table-artifact/table-artifact-content.constants.ts
490
+ var TABLE_STATUS = {
491
+ LOADING: "loading",
492
+ READY: "ready",
493
+ ERROR: "error",
494
+ UNAVAILABLE: "unavailable"
495
+ };
496
+
497
+ // src/components/artifacts/table-artifact/table-artifact-content.utils.ts
498
+ function formatCell(row, column) {
499
+ return row[column.key] ?? "";
500
+ }
501
+ var HEAD_CLASS = "h-auto py-2 px-4 text-xs font-medium text-foreground whitespace-nowrap text-left";
502
+ var CELL_CLASS = "p-4 text-sm font-normal text-foreground align-top min-w-[150px] text-left";
503
+ var ROW_CLASSES = "border-b-brown-30 bg-background/70 hover:bg-secondary";
504
+ var DATA_ROW_CLASSES = `${ROW_CLASSES} animate-in fade-in duration-300`;
505
+ var SCROLL_SHADOW = "0 1px 2px rgb(0 0 0 / 0.01), 0 6px 12px -2px rgb(0 0 0 / 0.03)";
506
+
507
+ // src/components/artifacts/table-artifact/table-artifact-content.tsx
508
+ import { jsx as jsx9, jsxs as jsxs3 } from "react/jsx-runtime";
509
+ function TableArtifactContent({
510
+ columns,
511
+ data,
512
+ knownRowCount,
513
+ maxShimmerRows,
514
+ maxBodyHeight,
515
+ labels
516
+ }) {
517
+ if (data.status === TABLE_STATUS.ERROR) {
518
+ return /* @__PURE__ */ jsx9(StatusMessage, { $tone: "error", children: labels.loadError });
519
+ }
520
+ if (data.status === TABLE_STATUS.UNAVAILABLE) {
521
+ return /* @__PURE__ */ jsx9(StatusMessage, { $tone: "info", children: labels.unavailable });
522
+ }
523
+ if (data.status === TABLE_STATUS.LOADING) {
524
+ if (typeof knownRowCount === "number" && knownRowCount > 0) {
525
+ const shimmerCount = Math.min(knownRowCount, maxShimmerRows);
526
+ return /* @__PURE__ */ jsx9(ArtifactTable, { columns, maxBodyHeight, children: renderShimmerRows(columns, shimmerCount) });
527
+ }
528
+ return /* @__PURE__ */ jsx9(StatusMessage, { $tone: "info", children: labels.loading });
529
+ }
530
+ return /* @__PURE__ */ jsx9(ArtifactTable, { columns, maxBodyHeight, children: data.rows.length === 0 ? renderEmptyRow(columns.length, labels.empty) : renderDataRows(columns, data.rows) });
531
+ }
532
+ function ArtifactTable({ columns, maxBodyHeight, children }) {
533
+ 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(
536
+ TableHeader,
537
+ {
538
+ "data-scrolled": isScrolled ? "true" : void 0,
539
+ className: "sticky top-0 z-10 bg-background/70 backdrop-blur-lg transition-shadow",
540
+ 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)) })
542
+ }
543
+ ),
544
+ /* @__PURE__ */ jsx9(TableBody, { children })
545
+ ] }) });
546
+ }
547
+ 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}`));
549
+ }
550
+ 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));
552
+ }
553
+ 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 }) });
555
+ }
556
+
557
+ // src/components/artifacts/table-artifact/use-table-artifact-data.ts
558
+ function useTableArtifactData(presignedUrl, columns) {
559
+ return useRowsArtifactData(presignedUrl, columns, parseCsv);
560
+ }
561
+
315
562
  // src/components/artifacts/table-artifact/validate.ts
316
563
  function isValidTableConfig(config) {
317
564
  if (!config || typeof config !== "object") return false;
@@ -329,42 +576,61 @@ function isValidTableColumn(column) {
329
576
  }
330
577
 
331
578
  // src/components/artifacts/table-artifact/table-artifact.tsx
332
- import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
333
- var TITLE_CLASSES = "text-sm font-semibold text-foreground";
579
+ import { jsx as jsx10, jsxs as jsxs4 } from "react/jsx-runtime";
580
+ var TITLE_CLASSES2 = "text-sm font-semibold text-foreground";
334
581
  var TABLE_MAX_BODY_HEIGHT = 480;
335
582
  var MAX_SHIMMER_ROWS = 10;
336
- var MSG_UNAVAILABLE = "Table data is unavailable.";
337
- function TableArtifact({ artifact }) {
583
+ function TableArtifact({ artifact, labels, callbacks }) {
338
584
  const validConfig = isValidTableConfig(artifact.config);
339
585
  const validColumns = validConfig ? artifact.config.columns : null;
340
- useEffect2(() => {
586
+ useEffect3(() => {
341
587
  if (validConfig) return;
342
588
  console.warn("[clay] TableArtifact: malformed `config` \u2014 skipping render.");
343
589
  }, [validConfig]);
590
+ const resolvedLabels = useMemo3(
591
+ () => ({ ...DEFAULT_TABLE_ARTIFACT_LABELS, ...labels?.[artifactTypes.TABLE] ?? {} }),
592
+ [labels]
593
+ );
344
594
  const data = useTableArtifactData(
345
595
  validConfig ? artifact.protectedAsset?.presignedUrl ?? null : null,
346
596
  validColumns ?? []
347
597
  );
348
598
  const presignedUrl = artifact.protectedAsset?.presignedUrl ?? null;
599
+ const onDownload = callbacks?.[artifactTypes.TABLE]?.onDownload;
600
+ const handleDownloadClick = onDownload ? () => onDownload(artifact) : void 0;
349
601
  if (!validConfig || !validColumns) {
350
- return /* @__PURE__ */ jsxs3(Artifact, { className: "shadow-none w-full rounded-2xl bg-background/70", children: [
351
- /* @__PURE__ */ jsxs3(ArtifactHeader, { className: "bg-background/70", children: [
352
- artifact.title ? /* @__PURE__ */ jsx5(ArtifactTitle, { className: TITLE_CLASSES, children: artifact.title }) : /* @__PURE__ */ jsx5("span", {}),
353
- presignedUrl ? /* @__PURE__ */ jsx5(ArtifactActions, { children: /* @__PURE__ */ jsx5(ArtifactDownloadButton, { href: presignedUrl }) }) : null
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(
606
+ ArtifactDownloadButton,
607
+ {
608
+ href: presignedUrl,
609
+ label: resolvedLabels.download,
610
+ onClick: handleDownloadClick
611
+ }
612
+ ) }) : null
354
613
  ] }),
355
- /* @__PURE__ */ jsx5(ArtifactContent, { className: "p-0 overflow-auto", style: { maxHeight: TABLE_MAX_BODY_HEIGHT }, children: /* @__PURE__ */ jsx5(StatusMessage, { $tone: "info", children: MSG_UNAVAILABLE }) })
614
+ /* @__PURE__ */ jsx10(ArtifactContent, { className: "p-0 overflow-auto", style: { maxHeight: TABLE_MAX_BODY_HEIGHT }, children: /* @__PURE__ */ jsx10(StatusMessage, { $tone: "info", children: resolvedLabels.unavailable }) })
356
615
  ] });
357
616
  }
358
617
  return (
359
618
  // The table body owns its own scroll container (`ArtifactTable` inside `TableArtifactContent`).
360
619
  // Disable the outer overflow container so only one scroll context exists — this prevents
361
620
  // double-scroll nesting and lets `useScrollShadow` find the correct scroll ancestor.
362
- /* @__PURE__ */ jsxs3(Artifact, { className: "shadow-none w-full rounded-2xl bg-background/70", children: [
363
- /* @__PURE__ */ jsxs3(ArtifactHeader, { className: "bg-background/70", children: [
364
- artifact.title ? /* @__PURE__ */ jsx5(ArtifactTitle, { className: TITLE_CLASSES, children: artifact.title }) : /* @__PURE__ */ jsx5("span", {}),
365
- /* @__PURE__ */ jsx5(ArtifactActions, { children: /* @__PURE__ */ jsx5(ArtifactDownloadButton, { href: presignedUrl ?? "" }) })
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(
625
+ ArtifactDownloadButton,
626
+ {
627
+ href: presignedUrl ?? "",
628
+ label: resolvedLabels.download,
629
+ onClick: handleDownloadClick
630
+ }
631
+ ) })
366
632
  ] }),
367
- /* @__PURE__ */ jsx5(ArtifactContent, { className: "p-0 overflow-visible", style: { maxHeight: "none" }, children: /* @__PURE__ */ jsx5(
633
+ /* @__PURE__ */ jsx10(ArtifactContent, { className: "p-0 overflow-visible", style: { maxHeight: "none" }, children: /* @__PURE__ */ jsx10(
368
634
  TableArtifactContent,
369
635
  {
370
636
  columns: validColumns,
@@ -372,13 +638,26 @@ function TableArtifact({ artifact }) {
372
638
  knownRowCount: artifact.config.stats?.rowCount,
373
639
  maxShimmerRows: MAX_SHIMMER_ROWS,
374
640
  maxBodyHeight: TABLE_MAX_BODY_HEIGHT,
375
- unavailableMessage: MSG_UNAVAILABLE
641
+ labels: resolvedLabels
376
642
  }
377
643
  ) })
378
644
  ] })
379
645
  );
380
646
  }
381
647
 
648
+ // src/components/artifacts/chart-artifact/to-clipboard-text.ts
649
+ function chartArtifactToClipboardText(artifact) {
650
+ const trimmedTitle = artifact.title?.trim();
651
+ return trimmedTitle || "Chart";
652
+ }
653
+
654
+ // src/components/artifacts/artifact-services/chart-artifact-service.ts
655
+ var ChartArtifactService = {
656
+ type: artifactTypes.CHART,
657
+ Component: ChartArtifact,
658
+ toClipboardText: chartArtifactToClipboardText
659
+ };
660
+
382
661
  // src/components/artifacts/table-artifact/to-clipboard-text.ts
383
662
  function tableArtifactToClipboardText(artifact) {
384
663
  const trimmedTitle = artifact.title?.trim();
@@ -394,7 +673,8 @@ var TableArtifactService = {
394
673
 
395
674
  // src/components/artifacts/artifact-services/index.ts
396
675
  var ArtifactServices = {
397
- [artifactTypes.TABLE]: TableArtifactService
676
+ [artifactTypes.TABLE]: TableArtifactService,
677
+ [artifactTypes.CHART]: ChartArtifactService
398
678
  };
399
679
  function artifactToClipboardText(artifact) {
400
680
  const entry = ArtifactServices[artifact.type];
@@ -406,12 +686,15 @@ function artifactToClipboardText(artifact) {
406
686
 
407
687
  export {
408
688
  artifactTypes,
689
+ DEFAULT_CHART_ARTIFACT_LABELS,
690
+ DEFAULT_TABLE_ARTIFACT_LABELS,
409
691
  IconCheck,
410
692
  IconCopy,
411
693
  IconThumbDown,
412
694
  IconThumbUp,
413
695
  ArtifactDownloadButton,
414
696
  isSafeDownloadUrl,
697
+ ChartArtifact,
415
698
  TableArtifact,
416
699
  ArtifactServices,
417
700
  artifactToClipboardText
@@ -433,4 +716,4 @@ export {
433
716
  * See the LICENSE file in the root directory of this source tree.
434
717
  *)
435
718
  */
436
- //# sourceMappingURL=chunk-BX5TCEPR.js.map
719
+ //# sourceMappingURL=chunk-Z5JWF24N.js.map