@shapesos/clay 0.17.0 → 0.18.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 (80) hide show
  1. package/dist/ai-elements.cjs +3 -1
  2. package/dist/ai-elements.cjs.map +1 -1
  3. package/dist/ai-elements.js +4 -4
  4. package/dist/artifacts.cjs +963 -34
  5. package/dist/artifacts.cjs.map +1 -1
  6. package/dist/artifacts.d.cts +20 -3
  7. package/dist/artifacts.d.ts +20 -3
  8. package/dist/artifacts.js +13 -5
  9. package/dist/blocks.cjs +677 -50
  10. package/dist/blocks.cjs.map +1 -1
  11. package/dist/blocks.css +1 -1
  12. package/dist/blocks.d.cts +3 -3
  13. package/dist/blocks.d.ts +3 -3
  14. package/dist/blocks.js +5 -5
  15. package/dist/button.cjs +3 -1
  16. package/dist/button.cjs.map +1 -1
  17. package/dist/button.js +2 -2
  18. package/dist/capture.cjs +3 -1
  19. package/dist/capture.cjs.map +1 -1
  20. package/dist/capture.js +2 -2
  21. package/dist/chart.cjs +3 -1
  22. package/dist/chart.cjs.map +1 -1
  23. package/dist/chart.js +2 -2
  24. package/dist/chat.cjs +701 -74
  25. package/dist/chat.cjs.map +1 -1
  26. package/dist/chat.d.cts +2 -2
  27. package/dist/chat.d.ts +2 -2
  28. package/dist/chat.js +6 -6
  29. package/dist/{chunk-46ZYTBX5.js → chunk-7MY4X4YL.js} +3 -3
  30. package/dist/chunk-BHHJOG5X.js +1 -0
  31. package/dist/{chunk-B6GAPZ2R.js → chunk-CATIRDZP.js} +5 -5
  32. package/dist/{chunk-B4U37WIH.js → chunk-CFCLVJIO.js} +2 -2
  33. package/dist/{chunk-PYDVANWI.js → chunk-E36VI2HA.js} +2 -2
  34. package/dist/{chunk-PUVZ2S3F.js → chunk-G4NXVFXU.js} +2 -2
  35. package/dist/{chunk-JAGNXEWK.js → chunk-GK4XW4BC.js} +2 -2
  36. package/dist/{chunk-4VE6ZXXW.js → chunk-MQP57DOP.js} +3 -3
  37. package/dist/{chunk-6YZ65KPW.js → chunk-QR4VBGK4.js} +673 -42
  38. package/dist/chunk-QR4VBGK4.js.map +1 -0
  39. package/dist/{chunk-JF3P66JF.js → chunk-SN3ORQSJ.js} +4 -2
  40. package/dist/{chunk-JF3P66JF.js.map → chunk-SN3ORQSJ.js.map} +1 -1
  41. package/dist/{chunk-PGR2MDDU.js → chunk-W66OJM55.js} +2 -2
  42. package/dist/{chunk-7OYIDM42.js → chunk-XKURJ72I.js} +2 -2
  43. package/dist/{dom-to-image-NATF3446.js → dom-to-image-QB7KMTAE.js} +3 -3
  44. package/dist/icon.cjs +3 -1
  45. package/dist/icon.cjs.map +1 -1
  46. package/dist/icon.js +2 -2
  47. package/dist/index.cjs +767 -132
  48. package/dist/index.cjs.map +1 -1
  49. package/dist/index.d.cts +3 -3
  50. package/dist/index.d.ts +3 -3
  51. package/dist/index.js +19 -11
  52. package/dist/keyboard-shortcut.cjs +3 -1
  53. package/dist/keyboard-shortcut.cjs.map +1 -1
  54. package/dist/keyboard-shortcut.js +2 -2
  55. package/dist/text-area.cjs +3 -1
  56. package/dist/text-area.cjs.map +1 -1
  57. package/dist/text-area.js +2 -2
  58. package/dist/tokens.cjs +3 -1
  59. package/dist/tokens.cjs.map +1 -1
  60. package/dist/tokens.d.cts +1 -0
  61. package/dist/tokens.d.ts +1 -0
  62. package/dist/tokens.js +1 -1
  63. package/dist/{types-CW45GBXl.d.cts → types-BDYPVP2N.d.cts} +1 -1
  64. package/dist/{types-D1b8v7EY.d.ts → types-BMwa4bPW.d.ts} +1 -1
  65. package/dist/{types-GCkXbyFm.d.ts → types-C8JtnwNY.d.ts} +200 -4
  66. package/dist/{types-BzXPBy6s.d.cts → types-DTdDFU3O.d.cts} +200 -4
  67. package/package.json +1 -1
  68. package/dist/chunk-6YZ65KPW.js.map +0 -1
  69. package/dist/chunk-RSRNSWJN.js +0 -1
  70. /package/dist/{chunk-46ZYTBX5.js.map → chunk-7MY4X4YL.js.map} +0 -0
  71. /package/dist/{chunk-RSRNSWJN.js.map → chunk-BHHJOG5X.js.map} +0 -0
  72. /package/dist/{chunk-B6GAPZ2R.js.map → chunk-CATIRDZP.js.map} +0 -0
  73. /package/dist/{chunk-B4U37WIH.js.map → chunk-CFCLVJIO.js.map} +0 -0
  74. /package/dist/{chunk-PYDVANWI.js.map → chunk-E36VI2HA.js.map} +0 -0
  75. /package/dist/{chunk-PUVZ2S3F.js.map → chunk-G4NXVFXU.js.map} +0 -0
  76. /package/dist/{chunk-JAGNXEWK.js.map → chunk-GK4XW4BC.js.map} +0 -0
  77. /package/dist/{chunk-4VE6ZXXW.js.map → chunk-MQP57DOP.js.map} +0 -0
  78. /package/dist/{chunk-PGR2MDDU.js.map → chunk-W66OJM55.js.map} +0 -0
  79. /package/dist/{chunk-7OYIDM42.js.map → chunk-XKURJ72I.js.map} +0 -0
  80. /package/dist/{dom-to-image-NATF3446.js.map → dom-to-image-QB7KMTAE.js.map} +0 -0
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  IconButton
3
- } from "./chunk-PYDVANWI.js";
3
+ } from "./chunk-E36VI2HA.js";
4
4
  import {
5
5
  CAPTURE_EXCLUDE_ATTRIBUTE
6
6
  } from "./chunk-LXYCT4YO.js";
@@ -8,7 +8,10 @@ import {
8
8
  BarChart,
9
9
  LineChart,
10
10
  PieChart
11
- } from "./chunk-PUVZ2S3F.js";
11
+ } from "./chunk-G4NXVFXU.js";
12
+ import {
13
+ fontFamilies
14
+ } from "./chunk-UXT2H3FM.js";
12
15
  import {
13
16
  Table,
14
17
  TableBody,
@@ -25,7 +28,8 @@ import {
25
28
  // src/components/artifacts/artifact-base.ts
26
29
  var artifactTypes = {
27
30
  TABLE: "TABLE",
28
- CHART: "CHART"
31
+ CHART: "CHART",
32
+ DECK: "DECK"
29
33
  };
30
34
 
31
35
  // src/components/artifacts/chart-artifact/types.ts
@@ -50,6 +54,34 @@ var DEFAULT_TABLE_ARTIFACT_LABELS = {
50
54
  empty: "No rows to display."
51
55
  };
52
56
 
57
+ // src/components/artifacts/deck-artifact/types.ts
58
+ var deckBlockTypes = {
59
+ TEXT: "TEXT",
60
+ CHART: "CHART",
61
+ TABLE: "TABLE"
62
+ };
63
+ var deckSlideKinds = {
64
+ TITLE: "TITLE",
65
+ AGENDA: "AGENDA",
66
+ SECTION: "SECTION",
67
+ CONTENT: "CONTENT",
68
+ CLOSING: "CLOSING"
69
+ };
70
+ var DEFAULT_DECK_ARTIFACT_LABELS = {
71
+ previous: "Previous slide",
72
+ next: "Next slide",
73
+ position: "Slide {current} of {total}",
74
+ download: "Download",
75
+ unavailable: "Deck content is unavailable.",
76
+ enterFullscreen: "Fullscreen",
77
+ exitFullscreen: "Exit fullscreen",
78
+ thankYou: "Thank you",
79
+ questions: "Questions?",
80
+ agenda: "Agenda",
81
+ chart: DEFAULT_CHART_ARTIFACT_LABELS,
82
+ table: DEFAULT_TABLE_ARTIFACT_LABELS
83
+ };
84
+
53
85
  // node_modules/@tabler/icons-react/dist/esm/createReactComponent.mjs
54
86
  import { forwardRef, createElement } from "react";
55
87
 
@@ -110,37 +142,53 @@ var createReactComponent = (type, iconName, iconNamePascal, iconNode) => {
110
142
  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
143
  var IconAlertCircle = createReactComponent("outline", "alert-circle", "AlertCircle", __iconNode);
112
144
 
145
+ // node_modules/@tabler/icons-react/dist/esm/icons/IconArrowsMaximize.mjs
146
+ var __iconNode2 = [["path", { "d": "M16 4l4 0l0 4", "key": "svg-0" }], ["path", { "d": "M14 10l6 -6", "key": "svg-1" }], ["path", { "d": "M8 20l-4 0l0 -4", "key": "svg-2" }], ["path", { "d": "M4 20l6 -6", "key": "svg-3" }], ["path", { "d": "M16 20l4 0l0 -4", "key": "svg-4" }], ["path", { "d": "M14 14l6 6", "key": "svg-5" }], ["path", { "d": "M8 4l-4 0l0 4", "key": "svg-6" }], ["path", { "d": "M4 4l6 6", "key": "svg-7" }]];
147
+ var IconArrowsMaximize = createReactComponent("outline", "arrows-maximize", "ArrowsMaximize", __iconNode2);
148
+
149
+ // node_modules/@tabler/icons-react/dist/esm/icons/IconArrowsMinimize.mjs
150
+ var __iconNode3 = [["path", { "d": "M5 9l4 0l0 -4", "key": "svg-0" }], ["path", { "d": "M3 3l6 6", "key": "svg-1" }], ["path", { "d": "M5 15l4 0l0 4", "key": "svg-2" }], ["path", { "d": "M3 21l6 -6", "key": "svg-3" }], ["path", { "d": "M19 9l-4 0l0 -4", "key": "svg-4" }], ["path", { "d": "M15 9l6 -6", "key": "svg-5" }], ["path", { "d": "M19 15l-4 0l0 4", "key": "svg-6" }], ["path", { "d": "M15 15l6 6", "key": "svg-7" }]];
151
+ var IconArrowsMinimize = createReactComponent("outline", "arrows-minimize", "ArrowsMinimize", __iconNode3);
152
+
113
153
  // node_modules/@tabler/icons-react/dist/esm/icons/IconCheck.mjs
114
- var __iconNode2 = [["path", { "d": "M5 12l5 5l10 -10", "key": "svg-0" }]];
115
- var IconCheck = createReactComponent("outline", "check", "Check", __iconNode2);
154
+ var __iconNode4 = [["path", { "d": "M5 12l5 5l10 -10", "key": "svg-0" }]];
155
+ var IconCheck = createReactComponent("outline", "check", "Check", __iconNode4);
156
+
157
+ // node_modules/@tabler/icons-react/dist/esm/icons/IconChevronLeft.mjs
158
+ var __iconNode5 = [["path", { "d": "M15 6l-6 6l6 6", "key": "svg-0" }]];
159
+ var IconChevronLeft = createReactComponent("outline", "chevron-left", "ChevronLeft", __iconNode5);
160
+
161
+ // node_modules/@tabler/icons-react/dist/esm/icons/IconChevronRight.mjs
162
+ var __iconNode6 = [["path", { "d": "M9 6l6 6l-6 6", "key": "svg-0" }]];
163
+ var IconChevronRight = createReactComponent("outline", "chevron-right", "ChevronRight", __iconNode6);
116
164
 
117
165
  // node_modules/@tabler/icons-react/dist/esm/icons/IconCopy.mjs
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);
166
+ var __iconNode7 = [["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" }]];
167
+ var IconCopy = createReactComponent("outline", "copy", "Copy", __iconNode7);
120
168
 
121
169
  // node_modules/@tabler/icons-react/dist/esm/icons/IconDownload.mjs
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);
170
+ var __iconNode8 = [["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" }]];
171
+ var IconDownload = createReactComponent("outline", "download", "Download", __iconNode8);
124
172
 
125
173
  // 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);
174
+ var __iconNode9 = [["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" }]];
175
+ var IconFileSpreadsheet = createReactComponent("outline", "file-spreadsheet", "FileSpreadsheet", __iconNode9);
128
176
 
129
177
  // 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);
178
+ var __iconNode10 = [["path", { "d": "M12 3a9 9 0 1 0 9 9", "key": "svg-0" }]];
179
+ var IconLoader2 = createReactComponent("outline", "loader-2", "Loader2", __iconNode10);
132
180
 
133
181
  // 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);
182
+ var __iconNode11 = [["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" }]];
183
+ var IconPhoto = createReactComponent("outline", "photo", "Photo", __iconNode11);
136
184
 
137
185
  // node_modules/@tabler/icons-react/dist/esm/icons/IconThumbDown.mjs
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);
186
+ var __iconNode12 = [["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" }]];
187
+ var IconThumbDown = createReactComponent("outline", "thumb-down", "ThumbDown", __iconNode12);
140
188
 
141
189
  // node_modules/@tabler/icons-react/dist/esm/icons/IconThumbUp.mjs
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);
190
+ var __iconNode13 = [["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" }]];
191
+ var IconThumbUp = createReactComponent("outline", "thumb-up", "ThumbUp", __iconNode13);
144
192
 
145
193
  // src/components/artifacts/artifact-copy-as-image-button/artifact-copy-as-image-button.tsx
146
194
  import { useCallback as useCallback2 } from "react";
@@ -203,7 +251,7 @@ function useCopyAsImage(targetRef, options = {}) {
203
251
  setError(null);
204
252
  setStatus("working");
205
253
  try {
206
- const { copyNodeAsPng } = await import("./dom-to-image-NATF3446.js");
254
+ const { copyNodeAsPng } = await import("./dom-to-image-QB7KMTAE.js");
207
255
  await copyNodeAsPng(node, nodeOptions);
208
256
  setStatus("success");
209
257
  scheduleReset();
@@ -382,7 +430,7 @@ function ArtifactDownloadMenu({
382
430
  if (!node) return;
383
431
  const filename = `${png.filename ?? DEFAULT_PNG_BASENAME}.png`;
384
432
  try {
385
- const { downloadNodeAsPng } = await import("./dom-to-image-NATF3446.js");
433
+ const { downloadNodeAsPng } = await import("./dom-to-image-QB7KMTAE.js");
386
434
  await downloadNodeAsPng(node, filename);
387
435
  } catch (err) {
388
436
  console.warn("[clay] ArtifactDownloadMenu: PNG download failed", err);
@@ -762,8 +810,12 @@ function renderBody(validConfig, artifact, data, labels) {
762
810
  return /* @__PURE__ */ jsx11(ChartArtifactContent, { config: artifact.config, rows: data.rows, labels });
763
811
  }
764
812
 
765
- // src/components/artifacts/table-artifact/table-artifact.tsx
766
- import { useEffect as useEffect4, useMemo as useMemo3 } from "react";
813
+ // src/components/artifacts/deck-artifact/deck-artifact.tsx
814
+ import { useEffect as useEffect4, useMemo as useMemo3, useState as useState5 } from "react";
815
+ import { createPortal } from "react-dom";
816
+
817
+ // src/components/artifacts/deck-artifact/deck-artifact-content.tsx
818
+ import { useCallback as useCallback4, useLayoutEffect as useLayoutEffect2, useRef as useRef4, useState as useState4 } from "react";
767
819
 
768
820
  // src/components/artifacts/table-artifact/table-artifact-content.constants.ts
769
821
  var TABLE_STATUS = {
@@ -833,9 +885,333 @@ function renderEmptyRow(colSpan, emptyMessage) {
833
885
  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 }) });
834
886
  }
835
887
 
836
- // src/components/artifacts/table-artifact/use-table-artifact-data.ts
837
- function useTableArtifactData(presignedUrl, columns) {
838
- return useRowsArtifactData(presignedUrl, columns, parseCsv);
888
+ // src/components/artifacts/deck-artifact/deck-markdown.tsx
889
+ import ReactMarkdown from "react-markdown";
890
+ import remarkBreaks from "remark-breaks";
891
+ import remarkGfm from "remark-gfm";
892
+ import { jsx as jsx13 } from "react/jsx-runtime";
893
+ function sanitize(text) {
894
+ return text.split("\n").filter((line) => {
895
+ const t = line.trim();
896
+ return !(t.startsWith("<!-- table-title:") && t.endsWith("-->"));
897
+ }).join("\n");
898
+ }
899
+ var MARKDOWN_COMPONENTS = {
900
+ h1: ({ node: _node, ...props }) => /* @__PURE__ */ jsx13("h1", { className: "text-[30px] font-semibold leading-tight text-foreground mb-3", ...props }),
901
+ h2: ({ node: _node, ...props }) => /* @__PURE__ */ jsx13("h2", { className: "text-[26px] font-semibold leading-tight text-foreground mb-2", ...props }),
902
+ h3: ({ node: _node, ...props }) => /* @__PURE__ */ jsx13("h3", { className: "text-[24px] font-semibold leading-tight text-foreground mb-2", ...props }),
903
+ h4: ({ node: _node, ...props }) => /* @__PURE__ */ jsx13("h4", { className: "text-[24px] font-semibold leading-tight text-foreground mb-2", ...props }),
904
+ h5: ({ node: _node, ...props }) => /* @__PURE__ */ jsx13("h5", { className: "text-[24px] font-semibold leading-tight text-foreground mb-2", ...props }),
905
+ h6: ({ node: _node, ...props }) => /* @__PURE__ */ jsx13("h6", { className: "text-[24px] font-semibold leading-tight text-foreground mb-2", ...props }),
906
+ p: ({ node: _node, ...props }) => /* @__PURE__ */ jsx13("p", { className: "text-[22px] leading-relaxed text-foreground my-2", ...props }),
907
+ ul: ({ node: _node, ...props }) => /* @__PURE__ */ jsx13("ul", { className: "list-disc pl-7 text-[22px] leading-relaxed text-foreground my-2", ...props }),
908
+ ol: ({ node: _node, ...props }) => /* @__PURE__ */ jsx13("ol", { className: "list-decimal pl-7 text-[22px] leading-relaxed text-foreground my-2", ...props }),
909
+ // react-markdown injects list metadata (`ordered` / `index` / `checked`) that must not leak to the DOM.
910
+ li: ({ node: _node, ordered: _ordered, index: _index, checked: _checked, ...props }) => /* @__PURE__ */ jsx13("li", { className: "my-1", ...props }),
911
+ strong: ({ node: _node, ...props }) => /* @__PURE__ */ jsx13("strong", { className: "font-semibold", ...props }),
912
+ em: ({ node: _node, ...props }) => /* @__PURE__ */ jsx13("em", { className: "italic", ...props }),
913
+ // Gate `<img src>` against an http(s) allowlist. react-markdown v8 percent-encodes the URL but
914
+ // does NOT protocol-filter image src (unlike link href), so an LLM-authored
915
+ // `![](javascript:…)` / `![](data:…)` or a tracking-pixel `![](http://…)` would otherwise render
916
+ // with the raw URL. Unsafe src → render nothing.
917
+ img: ({ node: _node, src, ...props }) => typeof src === "string" && isSafeDownloadUrl(src) ? /* @__PURE__ */ jsx13("img", { src, className: "max-w-full", ...props }) : null,
918
+ a: ({ node: _node, ...props }) => (
919
+ // `rel` is forced AFTER `{...props}` so it can't be overridden — if a consumer ever adds
920
+ // `linkTarget="_blank"`, LLM-authored links still can't reach `window.opener`.
921
+ /* @__PURE__ */ jsx13("a", { className: "underline underline-offset-2", ...props, rel: "noopener noreferrer" })
922
+ ),
923
+ // react-markdown passes `inline` to distinguish inline code from fenced blocks — strip it from the DOM.
924
+ code: ({ node: _node, inline: _inline, ...props }) => /* @__PURE__ */ jsx13("code", { className: "rounded bg-muted px-1.5 py-0.5 font-mono text-[18px]", ...props }),
925
+ blockquote: ({ node: _node, ...props }) => /* @__PURE__ */ jsx13("blockquote", { className: "border-l-2 border-brown-alpha-12 pl-4 text-[22px] text-muted-foreground", ...props }),
926
+ hr: ({ node: _node, ...props }) => /* @__PURE__ */ jsx13("hr", { className: "my-4 border-brown-alpha-12", ...props }),
927
+ table: ({ node: _node, ...props }) => /* @__PURE__ */ jsx13("div", { className: "overflow-x-auto", children: /* @__PURE__ */ jsx13("table", { className: "w-full border-collapse text-[20px]", ...props }) }),
928
+ // react-markdown passes `isHeader` to cell renderers — strip it so it never reaches the DOM.
929
+ th: ({ node: _node, isHeader: _isHeader, ...props }) => /* @__PURE__ */ jsx13("th", { className: "border-b border-brown-alpha-12 px-3 py-1.5 text-left font-medium", ...props }),
930
+ td: ({ node: _node, isHeader: _isHeader, ...props }) => /* @__PURE__ */ jsx13("td", { className: "border-b border-brown-alpha-12 px-3 py-1.5", ...props })
931
+ };
932
+ function DeckMarkdown({ markdown }) {
933
+ return /* @__PURE__ */ jsx13(ReactMarkdown, { remarkPlugins: [remarkGfm, remarkBreaks], components: MARKDOWN_COMPONENTS, children: sanitize(markdown) });
934
+ }
935
+
936
+ // src/components/artifacts/deck-artifact/slide-renderers.tsx
937
+ import { useLayoutEffect, useRef as useRef3, useState as useState3 } from "react";
938
+ import { jsx as jsx14, jsxs as jsxs6 } from "react/jsx-runtime";
939
+ function safeLogoUrl(meta) {
940
+ return meta?.logoUrl && isSafeDownloadUrl(meta.logoUrl) ? meta.logoUrl : null;
941
+ }
942
+ var DESIGN_W = 1280;
943
+ var DESIGN_H = 720;
944
+ var EYEBROW_H = 84;
945
+ var CANVAS_LOGO_MAX_H = 48;
946
+ var ACCENT_FONT = { fontFamily: `"${fontFamilies.CRIMSON_PRO}", serif`, fontStyle: "italic" };
947
+ function SlideFrame({ meta, children, accent = false, bare = false }) {
948
+ const frameRef = useRef3(null);
949
+ const [scale, setScale] = useState3(null);
950
+ useLayoutEffect(() => {
951
+ const el = frameRef.current;
952
+ if (!el) return;
953
+ const apply = (width) => {
954
+ if (width > 0) setScale(width / DESIGN_W);
955
+ };
956
+ apply(el.clientWidth);
957
+ const ro = new ResizeObserver((entries) => apply(entries[0]?.contentRect.width ?? 0));
958
+ ro.observe(el);
959
+ return () => ro.disconnect();
960
+ }, []);
961
+ const bg = accent ? "bg-indigo-600" : "bg-deck-canvas";
962
+ const eyebrowColor = accent ? "text-white/70" : "text-brown-60";
963
+ const borderColor = accent ? "border-white/20" : "border-brown-alpha-12";
964
+ const logoSrc = safeLogoUrl(meta);
965
+ return /* @__PURE__ */ jsx14("div", { ref: frameRef, className: `relative aspect-[16/9] w-full overflow-hidden rounded-lg ${bg}`, children: /* @__PURE__ */ jsxs6(
966
+ "div",
967
+ {
968
+ className: "absolute left-0 top-0 origin-top-left",
969
+ style: {
970
+ width: DESIGN_W,
971
+ height: DESIGN_H,
972
+ transform: `scale(${scale ?? 0})`,
973
+ visibility: scale === null ? "hidden" : "visible"
974
+ },
975
+ children: [
976
+ !bare ? /* @__PURE__ */ jsxs6(
977
+ "div",
978
+ {
979
+ className: `absolute inset-x-0 top-0 flex items-center justify-between border-b px-16 ${borderColor}`,
980
+ style: { height: EYEBROW_H },
981
+ children: [
982
+ meta?.eyebrow ? /* @__PURE__ */ jsx14("span", { className: eyebrowColor, style: { ...ACCENT_FONT, fontSize: 22 }, children: meta.eyebrow }) : /* @__PURE__ */ jsx14("span", {}),
983
+ logoSrc ? /* @__PURE__ */ jsx14("img", { src: logoSrc, alt: "", className: "object-contain", style: { maxHeight: CANVAS_LOGO_MAX_H } }) : null
984
+ ]
985
+ }
986
+ ) : null,
987
+ /* @__PURE__ */ jsx14("div", { className: "absolute inset-x-0 bottom-0 overflow-hidden", style: { top: bare ? 0 : EYEBROW_H }, children })
988
+ ]
989
+ }
990
+ ) });
991
+ }
992
+ function TitleSlide({ slide, meta }) {
993
+ const logoSrc = safeLogoUrl(meta);
994
+ return /* @__PURE__ */ jsx14(SlideFrame, { meta, bare: true, children: /* @__PURE__ */ jsxs6("div", { className: "flex h-full flex-col justify-center px-24 py-16", children: [
995
+ logoSrc ? /* @__PURE__ */ jsx14(
996
+ "img",
997
+ {
998
+ src: logoSrc,
999
+ alt: "",
1000
+ className: "mb-16 object-contain object-left",
1001
+ style: { maxHeight: 56, maxWidth: 380 }
1002
+ }
1003
+ ) : null,
1004
+ /* @__PURE__ */ jsx14(
1005
+ "h1",
1006
+ {
1007
+ className: "font-bold tracking-tight text-brown-100",
1008
+ style: { fontSize: 92, lineHeight: 1.04, maxWidth: 1060 },
1009
+ children: slide.title
1010
+ }
1011
+ ),
1012
+ slide.subtitle ? /* @__PURE__ */ jsx14("p", { className: "mt-7 text-brown-70", style: { ...ACCENT_FONT, fontSize: 34 }, children: slide.subtitle }) : null,
1013
+ slide.description ? /* @__PURE__ */ jsx14("p", { className: "mt-4 leading-relaxed text-brown-70", style: { fontSize: 24, maxWidth: 880 }, children: slide.description }) : null,
1014
+ meta?.date ? /* @__PURE__ */ jsx14("p", { className: "mt-10 text-brown-70", style: { ...ACCENT_FONT, fontSize: 30 }, children: meta.date }) : null
1015
+ ] }) });
1016
+ }
1017
+ function AgendaSlide({ slide, meta, labels }) {
1018
+ const items = slide.items ?? [];
1019
+ return /* @__PURE__ */ jsx14(SlideFrame, { meta, children: /* @__PURE__ */ jsxs6("div", { className: "h-full px-24 py-14", children: [
1020
+ /* @__PURE__ */ jsx14("h2", { className: "mb-10 font-bold text-brown-90", style: { fontSize: 40 }, children: slide.title || labels.agenda }),
1021
+ /* @__PURE__ */ jsx14("div", { className: "flex flex-col", children: items.map((item, i) => /* @__PURE__ */ jsxs6("div", { className: "flex items-baseline gap-6 border-b border-brown-alpha-12 py-5", children: [
1022
+ /* @__PURE__ */ jsx14("span", { className: "shrink-0 text-indigo-600", style: { ...ACCENT_FONT, fontSize: 28, width: 64 }, children: String(i + 1).padStart(2, "0") }),
1023
+ /* @__PURE__ */ jsx14("span", { className: "text-brown-90", style: { fontSize: 28 }, children: item })
1024
+ ] }, i)) })
1025
+ ] }) });
1026
+ }
1027
+ function SectionSlide({ slide, meta }) {
1028
+ return /* @__PURE__ */ jsx14(SlideFrame, { meta, accent: true, children: /* @__PURE__ */ jsxs6("div", { className: "flex h-full flex-col justify-center gap-6 px-24", children: [
1029
+ /* @__PURE__ */ jsx14("h2", { className: "font-bold text-white", style: { fontSize: 72, lineHeight: 1.05, maxWidth: 940 }, children: slide.title }),
1030
+ /* @__PURE__ */ jsx14("div", { className: "mt-2 bg-white/50", style: { width: 96, height: 5 } })
1031
+ ] }) });
1032
+ }
1033
+ function ClosingSlide({ meta, labels }) {
1034
+ return /* @__PURE__ */ jsx14(SlideFrame, { meta, children: /* @__PURE__ */ jsxs6("div", { className: "flex h-full flex-col items-center justify-center gap-6 px-24 text-center", children: [
1035
+ /* @__PURE__ */ jsx14("h1", { className: "font-bold text-brown-100", style: { fontSize: 72 }, children: labels.thankYou }),
1036
+ /* @__PURE__ */ jsx14("p", { className: "text-indigo-600", style: { ...ACCENT_FONT, fontSize: 36 }, children: labels.questions })
1037
+ ] }) });
1038
+ }
1039
+
1040
+ // src/components/artifacts/deck-artifact/deck-artifact-content.tsx
1041
+ import { jsx as jsx15, jsxs as jsxs7 } from "react/jsx-runtime";
1042
+ var TABLE_BLOCK_MAX_SHIMMER_ROWS = 10;
1043
+ var TABLE_CHROME_RESERVE = 56;
1044
+ var VISUAL_FALLBACK_HEIGHT = 360;
1045
+ function DeckArtifactContent({
1046
+ slides,
1047
+ labels,
1048
+ onNavigate,
1049
+ meta,
1050
+ onDark = false,
1051
+ currentSlide,
1052
+ onSlideChange
1053
+ }) {
1054
+ const navBtnClass = onDark ? "[&_svg]:!text-white hover:!bg-white/20 disabled:opacity-40" : void 0;
1055
+ const navBtnSize = onDark ? "medium" : "small";
1056
+ const isControlled = currentSlide !== void 0;
1057
+ const [internalIndex, setInternalIndex] = useState4(0);
1058
+ const index = isControlled ? currentSlide : internalIndex;
1059
+ const current = Math.min(index, slides.length - 1);
1060
+ const slide = slides[current];
1061
+ const goTo = useCallback4(
1062
+ (next) => {
1063
+ const clamped = Math.max(0, Math.min(next, slides.length - 1));
1064
+ if (isControlled) onSlideChange?.(clamped);
1065
+ else setInternalIndex(clamped);
1066
+ onNavigate?.(clamped);
1067
+ },
1068
+ [slides.length, onNavigate, isControlled, onSlideChange]
1069
+ );
1070
+ const position = labels.position.replace("{current}", String(current + 1)).replace("{total}", String(slides.length));
1071
+ return /* @__PURE__ */ jsxs7("div", { className: "flex flex-col gap-3 p-4", children: [
1072
+ /* @__PURE__ */ jsx15(SlideDispatch, { slide, labels, meta }),
1073
+ /* @__PURE__ */ jsxs7(
1074
+ "div",
1075
+ {
1076
+ className: `flex items-center justify-between border-t pt-2 ${onDark ? "border-white/20" : "border-brown-alpha-12"}`,
1077
+ children: [
1078
+ /* @__PURE__ */ jsx15(
1079
+ IconButton,
1080
+ {
1081
+ icon: IconChevronLeft,
1082
+ size: navBtnSize,
1083
+ className: navBtnClass,
1084
+ "aria-label": labels.previous,
1085
+ disabled: current === 0,
1086
+ onClick: () => goTo(current - 1)
1087
+ }
1088
+ ),
1089
+ /* @__PURE__ */ jsx15("span", { className: `text-xs font-medium ${onDark ? "text-white/70" : "text-brown-70"}`, children: position }),
1090
+ /* @__PURE__ */ jsx15(
1091
+ IconButton,
1092
+ {
1093
+ icon: IconChevronRight,
1094
+ size: navBtnSize,
1095
+ className: navBtnClass,
1096
+ "aria-label": labels.next,
1097
+ disabled: current === slides.length - 1,
1098
+ onClick: () => goTo(current + 1)
1099
+ }
1100
+ )
1101
+ ]
1102
+ }
1103
+ )
1104
+ ] });
1105
+ }
1106
+ function SlideDispatch({ slide, labels, meta }) {
1107
+ const kind = slide.kind ?? deckSlideKinds.CONTENT;
1108
+ switch (kind) {
1109
+ case deckSlideKinds.TITLE:
1110
+ return /* @__PURE__ */ jsx15(TitleSlide, { slide, meta });
1111
+ case deckSlideKinds.AGENDA:
1112
+ return /* @__PURE__ */ jsx15(AgendaSlide, { slide, meta, labels });
1113
+ case deckSlideKinds.SECTION:
1114
+ return /* @__PURE__ */ jsx15(SectionSlide, { slide, meta });
1115
+ case deckSlideKinds.CLOSING:
1116
+ return /* @__PURE__ */ jsx15(ClosingSlide, { meta, labels });
1117
+ case deckSlideKinds.CONTENT:
1118
+ default:
1119
+ return /* @__PURE__ */ jsx15(ContentSlide, { slide, meta, labels });
1120
+ }
1121
+ }
1122
+ function isVisualBlock(block) {
1123
+ return block.type === deckBlockTypes.CHART || block.type === deckBlockTypes.TABLE;
1124
+ }
1125
+ function ContentSlide({ slide, meta, labels }) {
1126
+ const content = slide.content ?? [];
1127
+ const visuals = content.filter(isVisualBlock);
1128
+ const texts = content.filter(
1129
+ (b) => b.type === deckBlockTypes.TEXT
1130
+ );
1131
+ if (visuals.length === 1 && texts.length > 0) {
1132
+ return /* @__PURE__ */ jsx15(SlideFrame, { meta, children: /* @__PURE__ */ jsxs7("div", { className: "flex h-full items-stretch gap-12 px-16 py-12", children: [
1133
+ /* @__PURE__ */ jsx15("div", { className: "flex w-[42%] shrink-0 flex-col overflow-hidden", children: /* @__PURE__ */ jsxs7(AutoFit, { children: [
1134
+ /* @__PURE__ */ jsx15("h3", { className: "mb-5 font-bold leading-tight text-foreground", style: { fontSize: 34 }, children: slide.title }),
1135
+ texts.map((block, i) => /* @__PURE__ */ jsx15(DeckMarkdown, { markdown: block.markdown }, i))
1136
+ ] }) }),
1137
+ /* @__PURE__ */ jsx15("div", { className: "min-h-0 min-w-0 flex-1", children: /* @__PURE__ */ jsx15(FillVisual, { block: visuals[0], labels }) })
1138
+ ] }) });
1139
+ }
1140
+ if (visuals.length > 0) {
1141
+ return /* @__PURE__ */ jsx15(SlideFrame, { meta, children: /* @__PURE__ */ jsxs7("div", { className: "flex h-full flex-col gap-5 px-16 py-10", children: [
1142
+ /* @__PURE__ */ jsx15("h3", { className: "shrink-0 font-bold leading-tight text-foreground", style: { fontSize: 32 }, children: slide.title }),
1143
+ content.map(
1144
+ (block, i) => isVisualBlock(block) ? /* @__PURE__ */ jsx15("div", { className: "min-h-0 min-w-0 flex-1", children: /* @__PURE__ */ jsx15(FillVisual, { block, labels }) }, i) : /* @__PURE__ */ jsx15("div", { className: "shrink-0 overflow-hidden", children: /* @__PURE__ */ jsx15(DeckMarkdown, { markdown: block.markdown }) }, i)
1145
+ )
1146
+ ] }) });
1147
+ }
1148
+ return /* @__PURE__ */ jsx15(SlideFrame, { meta, children: /* @__PURE__ */ jsx15("div", { className: "h-full px-24 py-12", children: /* @__PURE__ */ jsxs7(AutoFit, { children: [
1149
+ /* @__PURE__ */ jsx15("h3", { className: "mb-6 font-bold leading-tight text-foreground", style: { fontSize: 38 }, children: slide.title }),
1150
+ texts.map((block, i) => /* @__PURE__ */ jsx15(DeckMarkdown, { markdown: block.markdown }, i))
1151
+ ] }) }) });
1152
+ }
1153
+ function AutoFit({ children }) {
1154
+ const boxRef = useRef4(null);
1155
+ const contentRef = useRef4(null);
1156
+ const [fit, setFit] = useState4({ scale: 1, height: 0 });
1157
+ useLayoutEffect2(() => {
1158
+ const boxEl = boxRef.current;
1159
+ const contentEl = contentRef.current;
1160
+ if (!boxEl || !contentEl) return;
1161
+ const measure = () => {
1162
+ const avail = boxEl.clientHeight;
1163
+ const natural = contentEl.offsetHeight;
1164
+ const scale = avail > 0 && natural > avail ? avail / natural : 1;
1165
+ setFit({ scale, height: natural * scale });
1166
+ };
1167
+ measure();
1168
+ const ro = new ResizeObserver(measure);
1169
+ ro.observe(boxEl);
1170
+ ro.observe(contentEl);
1171
+ return () => ro.disconnect();
1172
+ }, []);
1173
+ const scaled = fit.scale < 1;
1174
+ return /* @__PURE__ */ jsx15("div", { ref: boxRef, className: "flex h-full min-h-0 w-full flex-col justify-center overflow-hidden", children: /* @__PURE__ */ jsx15("div", { style: scaled ? { height: fit.height } : void 0, children: /* @__PURE__ */ jsx15(
1175
+ "div",
1176
+ {
1177
+ ref: contentRef,
1178
+ className: "w-full origin-top-left",
1179
+ style: scaled ? { transform: `scale(${fit.scale})` } : void 0,
1180
+ children
1181
+ }
1182
+ ) }) });
1183
+ }
1184
+ function FillVisual({ block, labels }) {
1185
+ const ref = useRef4(null);
1186
+ const [height, setHeight] = useState4(VISUAL_FALLBACK_HEIGHT);
1187
+ useLayoutEffect2(() => {
1188
+ const el = ref.current;
1189
+ if (!el) return;
1190
+ const apply = (h) => {
1191
+ if (h > 0) setHeight(h);
1192
+ };
1193
+ apply(el.clientHeight);
1194
+ const ro = new ResizeObserver((entries) => apply(entries[0]?.contentRect.height ?? 0));
1195
+ ro.observe(el);
1196
+ return () => ro.disconnect();
1197
+ }, []);
1198
+ const chartLabels = labels.chart ?? DEFAULT_CHART_ARTIFACT_LABELS;
1199
+ return /* @__PURE__ */ jsx15("div", { ref, className: "h-full min-h-0 w-full overflow-hidden", children: block.type === deckBlockTypes.CHART ? (
1200
+ // ChartArtifactContent assumes a non-empty rowset (the standalone ChartArtifact guards
1201
+ // `rows.length > 0` upstream); mirror that guard here so an empty inline chart shows the
1202
+ // localized empty status instead of a blank chart area.
1203
+ block.rows.length === 0 ? /* @__PURE__ */ jsx15(StatusMessage, { $tone: "info", children: chartLabels.empty }) : /* @__PURE__ */ jsx15("div", { style: { height }, children: /* @__PURE__ */ jsx15(ChartArtifactContent, { config: block.config, rows: block.rows, labels: chartLabels }) })
1204
+ ) : /* @__PURE__ */ jsx15(
1205
+ TableArtifactContent,
1206
+ {
1207
+ columns: block.config.columns,
1208
+ data: { status: "ready", rows: block.rows, error: null },
1209
+ knownRowCount: block.config.stats?.rowCount ?? block.rows.length,
1210
+ maxShimmerRows: TABLE_BLOCK_MAX_SHIMMER_ROWS,
1211
+ maxBodyHeight: Math.max(0, height - TABLE_CHROME_RESERVE),
1212
+ labels: labels.table ?? DEFAULT_TABLE_ARTIFACT_LABELS
1213
+ }
1214
+ ) });
839
1215
  }
840
1216
 
841
1217
  // src/components/artifacts/table-artifact/validate.ts
@@ -854,19 +1230,206 @@ function isValidTableColumn(column) {
854
1230
  return true;
855
1231
  }
856
1232
 
857
- // src/components/artifacts/table-artifact/table-artifact.tsx
858
- import { jsx as jsx13, jsxs as jsxs6 } from "react/jsx-runtime";
1233
+ // src/components/artifacts/deck-artifact/validate.ts
1234
+ function isValidDeckConfig(config) {
1235
+ if (!config || typeof config !== "object") return false;
1236
+ const candidate = config;
1237
+ if (!Array.isArray(candidate.slides)) return false;
1238
+ if (candidate.slides.length === 0) return false;
1239
+ return candidate.slides.every(isValidDeckSlide);
1240
+ }
1241
+ function isValidDeckSlide(slide) {
1242
+ if (!slide || typeof slide !== "object") return false;
1243
+ const candidate = slide;
1244
+ if (typeof candidate.title !== "string") return false;
1245
+ const kind = typeof candidate.kind === "string" ? candidate.kind : deckSlideKinds.CONTENT;
1246
+ if (kind === deckSlideKinds.CONTENT && !Array.isArray(candidate.content)) return false;
1247
+ const content = Array.isArray(candidate.content) ? candidate.content : [];
1248
+ if (candidate.items != null) {
1249
+ if (!Array.isArray(candidate.items)) return false;
1250
+ if (!candidate.items.every((item) => typeof item === "string")) return false;
1251
+ }
1252
+ return content.every(isValidDeckBlock);
1253
+ }
1254
+ function isValidDeckBlock(block) {
1255
+ if (!block || typeof block !== "object") return false;
1256
+ const candidate = block;
1257
+ switch (candidate.type) {
1258
+ case deckBlockTypes.TEXT:
1259
+ return typeof candidate.markdown === "string";
1260
+ case deckBlockTypes.CHART:
1261
+ return isValidChartConfig(candidate.config) && Array.isArray(candidate.rows);
1262
+ case deckBlockTypes.TABLE:
1263
+ return isValidTableConfig(candidate.config) && Array.isArray(candidate.rows);
1264
+ default:
1265
+ return false;
1266
+ }
1267
+ }
1268
+
1269
+ // src/components/artifacts/deck-artifact/deck-artifact.tsx
1270
+ import { Fragment, jsx as jsx16, jsxs as jsxs8 } from "react/jsx-runtime";
859
1271
  var TITLE_CLASSES2 = "text-sm font-semibold text-foreground";
1272
+ function DeckArtifact({ artifact, labels, callbacks }) {
1273
+ const validConfig = isValidDeckConfig(artifact.config);
1274
+ const [isFullscreen, setIsFullscreen] = useState5(false);
1275
+ const [currentSlide, setCurrentSlide] = useState5(0);
1276
+ useEffect4(() => {
1277
+ if (validConfig) return;
1278
+ console.warn("[clay] DeckArtifact: malformed `config` \u2014 skipping render.");
1279
+ }, [validConfig]);
1280
+ useEffect4(() => {
1281
+ if (!isFullscreen) return;
1282
+ const handleKey = (e) => {
1283
+ if (e.key === "Escape") setIsFullscreen(false);
1284
+ };
1285
+ document.addEventListener("keydown", handleKey);
1286
+ const previousOverflow = document.documentElement.style.overflow;
1287
+ document.documentElement.style.overflow = "hidden";
1288
+ return () => {
1289
+ document.removeEventListener("keydown", handleKey);
1290
+ document.documentElement.style.overflow = previousOverflow;
1291
+ };
1292
+ }, [isFullscreen]);
1293
+ const resolvedLabels = useMemo3(
1294
+ () => ({
1295
+ ...DEFAULT_DECK_ARTIFACT_LABELS,
1296
+ ...labels ?? {},
1297
+ chart: { ...DEFAULT_CHART_ARTIFACT_LABELS, ...labels?.chart ?? {} },
1298
+ table: { ...DEFAULT_TABLE_ARTIFACT_LABELS, ...labels?.table ?? {} }
1299
+ }),
1300
+ [labels]
1301
+ );
1302
+ const onNavigate = callbacks?.onNavigate;
1303
+ const handleNavigate = onNavigate ? (slideIndex) => onNavigate(artifact, slideIndex) : void 0;
1304
+ const onDownload = callbacks?.onDownload;
1305
+ const handleDownload = onDownload ? () => onDownload(artifact) : void 0;
1306
+ const presignedUrl = artifact.protectedAsset?.presignedUrl ?? null;
1307
+ const showDownload = presignedUrl !== null && isSafeDownloadUrl(presignedUrl);
1308
+ const meta = artifact.config?.meta;
1309
+ const handleOpenFullscreen = () => setIsFullscreen(true);
1310
+ const handleCloseFullscreen = () => setIsFullscreen(false);
1311
+ const actions = /* @__PURE__ */ jsxs8(ArtifactActions, { ...{ [CAPTURE_EXCLUDE_ATTRIBUTE]: "" }, children: [
1312
+ showDownload ? /* @__PURE__ */ jsx16(ArtifactDownloadButton, { href: presignedUrl ?? "", label: resolvedLabels.download, onClick: handleDownload }) : null,
1313
+ validConfig ? /* @__PURE__ */ jsx16(
1314
+ IconButton,
1315
+ {
1316
+ icon: IconArrowsMaximize,
1317
+ size: "small",
1318
+ "aria-label": resolvedLabels.enterFullscreen,
1319
+ onClick: handleOpenFullscreen
1320
+ }
1321
+ ) : null
1322
+ ] });
1323
+ if (!validConfig) {
1324
+ return /* @__PURE__ */ jsxs8(Artifact, { className: "shadow-none w-full rounded-2xl bg-background/70", children: [
1325
+ /* @__PURE__ */ jsxs8(ArtifactHeader, { className: "bg-background/70", children: [
1326
+ artifact.title ? /* @__PURE__ */ jsx16(ArtifactTitle, { className: TITLE_CLASSES2, children: artifact.title }) : /* @__PURE__ */ jsx16("span", {}),
1327
+ actions
1328
+ ] }),
1329
+ /* @__PURE__ */ jsx16(ArtifactContent, { className: "p-0", children: /* @__PURE__ */ jsx16(StatusMessage, { $tone: "info", children: resolvedLabels.unavailable }) })
1330
+ ] });
1331
+ }
1332
+ return /* @__PURE__ */ jsxs8(Fragment, { children: [
1333
+ /* @__PURE__ */ jsxs8(Artifact, { className: "shadow-none w-full rounded-2xl bg-background/70", children: [
1334
+ /* @__PURE__ */ jsxs8(ArtifactHeader, { className: "bg-background/70", children: [
1335
+ artifact.title ? /* @__PURE__ */ jsx16(ArtifactTitle, { className: TITLE_CLASSES2, children: artifact.title }) : /* @__PURE__ */ jsx16("span", {}),
1336
+ actions
1337
+ ] }),
1338
+ /* @__PURE__ */ jsx16(ArtifactContent, { className: "p-0", children: /* @__PURE__ */ jsx16(
1339
+ DeckArtifactContent,
1340
+ {
1341
+ slides: artifact.config.slides,
1342
+ labels: resolvedLabels,
1343
+ onNavigate: handleNavigate,
1344
+ meta,
1345
+ currentSlide,
1346
+ onSlideChange: setCurrentSlide
1347
+ }
1348
+ ) })
1349
+ ] }),
1350
+ isFullscreen && createPortal(
1351
+ /* @__PURE__ */ jsx16(
1352
+ FullscreenOverlay,
1353
+ {
1354
+ artifact,
1355
+ resolvedLabels,
1356
+ onNavigate: handleNavigate,
1357
+ currentSlide,
1358
+ onSlideChange: setCurrentSlide,
1359
+ onClose: handleCloseFullscreen
1360
+ }
1361
+ ),
1362
+ document.body
1363
+ )
1364
+ ] });
1365
+ }
1366
+ function FullscreenOverlay({
1367
+ artifact,
1368
+ resolvedLabels,
1369
+ onNavigate,
1370
+ currentSlide,
1371
+ onSlideChange,
1372
+ onClose
1373
+ }) {
1374
+ const meta = artifact.config?.meta;
1375
+ return /* @__PURE__ */ jsx16(
1376
+ "div",
1377
+ {
1378
+ className: "fixed inset-0 z-[2147483647] flex items-center justify-center bg-black/80",
1379
+ role: "dialog",
1380
+ "aria-modal": "true",
1381
+ "aria-label": resolvedLabels.exitFullscreen,
1382
+ children: /* @__PURE__ */ jsxs8("div", { className: "relative", style: { width: "min(94vw, calc(82vh * 16 / 9))" }, children: [
1383
+ /* @__PURE__ */ jsx16(
1384
+ DeckArtifactContent,
1385
+ {
1386
+ slides: artifact.config.slides,
1387
+ labels: resolvedLabels,
1388
+ onNavigate,
1389
+ meta,
1390
+ currentSlide,
1391
+ onSlideChange,
1392
+ onDark: true
1393
+ }
1394
+ ),
1395
+ /* @__PURE__ */ jsxs8(
1396
+ "button",
1397
+ {
1398
+ className: "absolute -top-10 right-6 flex items-center gap-1.5 text-sm text-white/80 hover:text-white",
1399
+ "aria-label": resolvedLabels.exitFullscreen,
1400
+ onClick: onClose,
1401
+ children: [
1402
+ /* @__PURE__ */ jsx16(IconArrowsMinimize, { size: 16 }),
1403
+ /* @__PURE__ */ jsx16("span", { children: resolvedLabels.exitFullscreen })
1404
+ ]
1405
+ }
1406
+ )
1407
+ ] })
1408
+ }
1409
+ );
1410
+ }
1411
+
1412
+ // src/components/artifacts/table-artifact/table-artifact.tsx
1413
+ import { useEffect as useEffect5, useMemo as useMemo4 } from "react";
1414
+
1415
+ // src/components/artifacts/table-artifact/use-table-artifact-data.ts
1416
+ function useTableArtifactData(presignedUrl, columns) {
1417
+ return useRowsArtifactData(presignedUrl, columns, parseCsv);
1418
+ }
1419
+
1420
+ // src/components/artifacts/table-artifact/table-artifact.tsx
1421
+ import { jsx as jsx17, jsxs as jsxs9 } from "react/jsx-runtime";
1422
+ var TITLE_CLASSES3 = "text-sm font-semibold text-foreground";
860
1423
  var TABLE_MAX_BODY_HEIGHT = 480;
861
1424
  var MAX_SHIMMER_ROWS = 10;
862
1425
  function TableArtifact({ artifact, labels, callbacks }) {
863
1426
  const validConfig = isValidTableConfig(artifact.config);
864
1427
  const validColumns = validConfig ? artifact.config.columns : null;
865
- useEffect4(() => {
1428
+ useEffect5(() => {
866
1429
  if (validConfig) return;
867
1430
  console.warn("[clay] TableArtifact: malformed `config` \u2014 skipping render.");
868
1431
  }, [validConfig]);
869
- const resolvedLabels = useMemo3(
1432
+ const resolvedLabels = useMemo4(
870
1433
  () => ({ ...DEFAULT_TABLE_ARTIFACT_LABELS, ...labels ?? {} }),
871
1434
  [labels]
872
1435
  );
@@ -878,10 +1441,10 @@ function TableArtifact({ artifact, labels, callbacks }) {
878
1441
  const onDownload = callbacks?.onDownload;
879
1442
  const handleDownloadClick = onDownload ? () => onDownload(artifact) : void 0;
880
1443
  if (!validConfig || !validColumns) {
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(
1444
+ return /* @__PURE__ */ jsxs9(Artifact, { className: "shadow-none w-full rounded-2xl bg-background/70", children: [
1445
+ /* @__PURE__ */ jsxs9(ArtifactHeader, { className: "bg-background/70", children: [
1446
+ artifact.title ? /* @__PURE__ */ jsx17(ArtifactTitle, { className: TITLE_CLASSES3, children: artifact.title }) : /* @__PURE__ */ jsx17("span", {}),
1447
+ presignedUrl ? /* @__PURE__ */ jsx17(ArtifactActions, { children: /* @__PURE__ */ jsx17(
885
1448
  ArtifactDownloadButton,
886
1449
  {
887
1450
  href: presignedUrl,
@@ -890,17 +1453,17 @@ function TableArtifact({ artifact, labels, callbacks }) {
890
1453
  }
891
1454
  ) }) : null
892
1455
  ] }),
893
- /* @__PURE__ */ jsx13(ArtifactContent, { className: "p-0 overflow-auto", style: { maxHeight: TABLE_MAX_BODY_HEIGHT }, children: /* @__PURE__ */ jsx13(StatusMessage, { $tone: "info", children: resolvedLabels.unavailable }) })
1456
+ /* @__PURE__ */ jsx17(ArtifactContent, { className: "p-0 overflow-auto", style: { maxHeight: TABLE_MAX_BODY_HEIGHT }, children: /* @__PURE__ */ jsx17(StatusMessage, { $tone: "info", children: resolvedLabels.unavailable }) })
894
1457
  ] });
895
1458
  }
896
1459
  return (
897
1460
  // The table body owns its own scroll container (`ArtifactTable` inside `TableArtifactContent`).
898
1461
  // Disable the outer overflow container so only one scroll context exists — this prevents
899
1462
  // double-scroll nesting and lets `useScrollShadow` find the correct scroll ancestor.
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(
1463
+ /* @__PURE__ */ jsxs9(Artifact, { className: "shadow-none w-full rounded-2xl bg-background/70", children: [
1464
+ /* @__PURE__ */ jsxs9(ArtifactHeader, { className: "bg-background/70", children: [
1465
+ artifact.title ? /* @__PURE__ */ jsx17(ArtifactTitle, { className: TITLE_CLASSES3, children: artifact.title }) : /* @__PURE__ */ jsx17("span", {}),
1466
+ /* @__PURE__ */ jsx17(ArtifactActions, { children: /* @__PURE__ */ jsx17(
904
1467
  ArtifactDownloadButton,
905
1468
  {
906
1469
  href: presignedUrl ?? "",
@@ -909,7 +1472,7 @@ function TableArtifact({ artifact, labels, callbacks }) {
909
1472
  }
910
1473
  ) })
911
1474
  ] }),
912
- /* @__PURE__ */ jsx13(ArtifactContent, { className: "p-0 overflow-visible", style: { maxHeight: "none" }, children: /* @__PURE__ */ jsx13(
1475
+ /* @__PURE__ */ jsx17(ArtifactContent, { className: "p-0 overflow-visible", style: { maxHeight: "none" }, children: /* @__PURE__ */ jsx17(
913
1476
  TableArtifactContent,
914
1477
  {
915
1478
  columns: validColumns,
@@ -937,6 +1500,65 @@ var ChartArtifactService = {
937
1500
  toClipboardText: chartArtifactToClipboardText
938
1501
  };
939
1502
 
1503
+ // src/components/artifacts/deck-artifact/to-clipboard-text.ts
1504
+ function deckArtifactToClipboardText(artifact) {
1505
+ const header = artifact.title?.trim() || "Deck";
1506
+ const slides = artifact.config?.slides ?? [];
1507
+ const body = slides.map((slide, i) => slideToText(slide, i)).join("\n\n");
1508
+ return body ? `${header}
1509
+
1510
+ ${body}` : header;
1511
+ }
1512
+ function slideToText(slide, index) {
1513
+ const kind = slide.kind ?? deckSlideKinds.CONTENT;
1514
+ const prefix = `Slide ${index + 1}: `;
1515
+ switch (kind) {
1516
+ case deckSlideKinds.TITLE: {
1517
+ const parts = [slide.title];
1518
+ if (slide.subtitle) parts.push(slide.subtitle);
1519
+ if (slide.description) parts.push(slide.description);
1520
+ return `${prefix}${parts.join(" \u2014 ")}`.trim();
1521
+ }
1522
+ case deckSlideKinds.AGENDA: {
1523
+ const items = (slide.items ?? []).map((item, i) => ` ${String(i + 1).padStart(2, "0")}. ${item}`).join("\n");
1524
+ return `${prefix}${slide.title || "Agenda"}${items ? `
1525
+ ${items}` : ""}`.trim();
1526
+ }
1527
+ case deckSlideKinds.SECTION:
1528
+ return `${prefix}${slide.title}`.trim();
1529
+ case deckSlideKinds.CLOSING:
1530
+ return `${prefix}Thank you`.trim();
1531
+ case deckSlideKinds.CONTENT:
1532
+ default: {
1533
+ const blocks = (slide.content ?? []).map(blockToText).filter(Boolean).join("\n\n");
1534
+ return `${prefix}${slide.title}${blocks ? `
1535
+ ${blocks}` : ""}`.trim();
1536
+ }
1537
+ }
1538
+ }
1539
+ function blockToText(block) {
1540
+ switch (block.type) {
1541
+ case deckBlockTypes.TEXT:
1542
+ return block.markdown.trim();
1543
+ case deckBlockTypes.CHART:
1544
+ return `[Chart: ${block.config.variant}]`;
1545
+ case deckBlockTypes.TABLE: {
1546
+ const columnCount = block.config.columns.length;
1547
+ const rowCount = block.rows.length;
1548
+ return `[Table: ${columnCount} column${columnCount === 1 ? "" : "s"} \xD7 ${rowCount} row${rowCount === 1 ? "" : "s"}]`;
1549
+ }
1550
+ default:
1551
+ return "";
1552
+ }
1553
+ }
1554
+
1555
+ // src/components/artifacts/artifact-services/deck-artifact-service.ts
1556
+ var DeckArtifactService = {
1557
+ type: artifactTypes.DECK,
1558
+ Component: DeckArtifact,
1559
+ toClipboardText: deckArtifactToClipboardText
1560
+ };
1561
+
940
1562
  // src/components/artifacts/table-artifact/to-clipboard-text.ts
941
1563
  function tableArtifactToClipboardText(artifact) {
942
1564
  const trimmedTitle = artifact.title?.trim();
@@ -953,7 +1575,8 @@ var TableArtifactService = {
953
1575
  // src/components/artifacts/artifact-services/index.ts
954
1576
  var ArtifactServices = {
955
1577
  [artifactTypes.TABLE]: TableArtifactService,
956
- [artifactTypes.CHART]: ChartArtifactService
1578
+ [artifactTypes.CHART]: ChartArtifactService,
1579
+ [artifactTypes.DECK]: DeckArtifactService
957
1580
  };
958
1581
  function artifactToClipboardText(artifact) {
959
1582
  const entry = ArtifactServices[artifact.type];
@@ -967,6 +1590,9 @@ export {
967
1590
  artifactTypes,
968
1591
  DEFAULT_CHART_ARTIFACT_LABELS,
969
1592
  DEFAULT_TABLE_ARTIFACT_LABELS,
1593
+ deckBlockTypes,
1594
+ deckSlideKinds,
1595
+ DEFAULT_DECK_ARTIFACT_LABELS,
970
1596
  IconCheck,
971
1597
  IconCopy,
972
1598
  IconThumbDown,
@@ -976,6 +1602,7 @@ export {
976
1602
  isSafeDownloadUrl,
977
1603
  ArtifactDownloadMenu,
978
1604
  ChartArtifact,
1605
+ DeckArtifact,
979
1606
  TableArtifact,
980
1607
  ArtifactServices,
981
1608
  artifactToClipboardText
@@ -985,7 +1612,11 @@ export {
985
1612
  @tabler/icons-react/dist/esm/defaultAttributes.mjs:
986
1613
  @tabler/icons-react/dist/esm/createReactComponent.mjs:
987
1614
  @tabler/icons-react/dist/esm/icons/IconAlertCircle.mjs:
1615
+ @tabler/icons-react/dist/esm/icons/IconArrowsMaximize.mjs:
1616
+ @tabler/icons-react/dist/esm/icons/IconArrowsMinimize.mjs:
988
1617
  @tabler/icons-react/dist/esm/icons/IconCheck.mjs:
1618
+ @tabler/icons-react/dist/esm/icons/IconChevronLeft.mjs:
1619
+ @tabler/icons-react/dist/esm/icons/IconChevronRight.mjs:
989
1620
  @tabler/icons-react/dist/esm/icons/IconCopy.mjs:
990
1621
  @tabler/icons-react/dist/esm/icons/IconDownload.mjs:
991
1622
  @tabler/icons-react/dist/esm/icons/IconFileSpreadsheet.mjs:
@@ -1001,4 +1632,4 @@ export {
1001
1632
  * See the LICENSE file in the root directory of this source tree.
1002
1633
  *)
1003
1634
  */
1004
- //# sourceMappingURL=chunk-6YZ65KPW.js.map
1635
+ //# sourceMappingURL=chunk-QR4VBGK4.js.map