@shapesos/clay 0.17.0 → 0.19.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 (111) 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 +1010 -60
  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 +15 -6
  9. package/dist/badge.cjs +70 -0
  10. package/dist/badge.cjs.map +1 -0
  11. package/dist/badge.d.cts +28 -0
  12. package/dist/badge.d.ts +28 -0
  13. package/dist/badge.js +9 -0
  14. package/dist/blocks.cjs +981 -96
  15. package/dist/blocks.cjs.map +1 -1
  16. package/dist/blocks.css +1 -1
  17. package/dist/blocks.d.cts +15 -4
  18. package/dist/blocks.d.ts +15 -4
  19. package/dist/blocks.js +20 -8
  20. package/dist/button.cjs +3 -1
  21. package/dist/button.cjs.map +1 -1
  22. package/dist/button.js +2 -2
  23. package/dist/capture.cjs +3 -1
  24. package/dist/capture.cjs.map +1 -1
  25. package/dist/capture.js +2 -2
  26. package/dist/chart.cjs +3 -1
  27. package/dist/chart.cjs.map +1 -1
  28. package/dist/chart.js +2 -2
  29. package/dist/chat.cjs +986 -151
  30. package/dist/chat.cjs.map +1 -1
  31. package/dist/chat.d.cts +2 -2
  32. package/dist/chat.d.ts +2 -2
  33. package/dist/chat.js +13 -8
  34. package/dist/{chunk-JGMN6W72.js → chunk-2X4KUXYP.js} +1 -1
  35. package/dist/chunk-BHHJOG5X.js +1 -0
  36. package/dist/{chunk-B4U37WIH.js → chunk-CFCLVJIO.js} +2 -2
  37. package/dist/chunk-CSTCTO3B.js +161 -0
  38. package/dist/chunk-CSTCTO3B.js.map +1 -0
  39. package/dist/{chunk-6YZ65KPW.js → chunk-DC5KYT2G.js} +669 -171
  40. package/dist/chunk-DC5KYT2G.js.map +1 -0
  41. package/dist/{chunk-PYDVANWI.js → chunk-E36VI2HA.js} +2 -2
  42. package/dist/chunk-EIAWZXE4.js +69 -0
  43. package/dist/chunk-EIAWZXE4.js.map +1 -0
  44. package/dist/chunk-EXRNGIQ2.js +41 -0
  45. package/dist/chunk-EXRNGIQ2.js.map +1 -0
  46. package/dist/{chunk-B6GAPZ2R.js → chunk-FCSYVF4Z.js} +12 -46
  47. package/dist/chunk-FCSYVF4Z.js.map +1 -0
  48. package/dist/{chunk-PUVZ2S3F.js → chunk-G4NXVFXU.js} +2 -2
  49. package/dist/{chunk-JAGNXEWK.js → chunk-GK4XW4BC.js} +2 -2
  50. package/dist/{chunk-4MZZH3WX.js → chunk-HU5W7CGE.js} +37 -2
  51. package/dist/chunk-HU5W7CGE.js.map +1 -0
  52. package/dist/chunk-JMHE3Q5K.js +406 -0
  53. package/dist/chunk-JMHE3Q5K.js.map +1 -0
  54. package/dist/{chunk-4VE6ZXXW.js → chunk-MQP57DOP.js} +3 -3
  55. package/dist/{chunk-JF3P66JF.js → chunk-SN3ORQSJ.js} +4 -2
  56. package/dist/{chunk-JF3P66JF.js.map → chunk-SN3ORQSJ.js.map} +1 -1
  57. package/dist/{chunk-PGR2MDDU.js → chunk-W66OJM55.js} +2 -2
  58. package/dist/{chunk-7OYIDM42.js → chunk-XKURJ72I.js} +2 -2
  59. package/dist/copy-button.cjs +439 -0
  60. package/dist/copy-button.cjs.map +1 -0
  61. package/dist/copy-button.d.cts +20 -0
  62. package/dist/copy-button.d.ts +20 -0
  63. package/dist/copy-button.js +12 -0
  64. package/dist/copy-button.js.map +1 -0
  65. package/dist/{dom-to-image-NATF3446.js → dom-to-image-QB7KMTAE.js} +3 -3
  66. package/dist/dom-to-image-QB7KMTAE.js.map +1 -0
  67. package/dist/icon.cjs +3 -1
  68. package/dist/icon.cjs.map +1 -1
  69. package/dist/icon.js +2 -2
  70. package/dist/index.cjs +1070 -209
  71. package/dist/index.cjs.map +1 -1
  72. package/dist/index.d.cts +6 -4
  73. package/dist/index.d.ts +6 -4
  74. package/dist/index.js +42 -17
  75. package/dist/keyboard-shortcut.cjs +3 -1
  76. package/dist/keyboard-shortcut.cjs.map +1 -1
  77. package/dist/keyboard-shortcut.js +2 -2
  78. package/dist/table.cjs +40 -0
  79. package/dist/table.cjs.map +1 -1
  80. package/dist/table.d.cts +37 -2
  81. package/dist/table.d.ts +37 -2
  82. package/dist/table.js +11 -1
  83. package/dist/text-area.cjs +3 -1
  84. package/dist/text-area.cjs.map +1 -1
  85. package/dist/text-area.js +2 -2
  86. package/dist/tokens.cjs +3 -1
  87. package/dist/tokens.cjs.map +1 -1
  88. package/dist/tokens.d.cts +1 -0
  89. package/dist/tokens.d.ts +1 -0
  90. package/dist/tokens.js +1 -1
  91. package/dist/{types-CW45GBXl.d.cts → types-Bhg0_ES8.d.cts} +75 -3
  92. package/dist/{types-GCkXbyFm.d.ts → types-C8JtnwNY.d.ts} +200 -4
  93. package/dist/{types-BzXPBy6s.d.cts → types-DTdDFU3O.d.cts} +200 -4
  94. package/dist/{types-D1b8v7EY.d.ts → types-orOQcI2z.d.ts} +75 -3
  95. package/package.json +11 -1
  96. package/dist/chunk-46ZYTBX5.js +0 -250
  97. package/dist/chunk-46ZYTBX5.js.map +0 -1
  98. package/dist/chunk-4MZZH3WX.js.map +0 -1
  99. package/dist/chunk-6YZ65KPW.js.map +0 -1
  100. package/dist/chunk-B6GAPZ2R.js.map +0 -1
  101. package/dist/chunk-RSRNSWJN.js +0 -1
  102. /package/dist/{chunk-RSRNSWJN.js.map → badge.js.map} +0 -0
  103. /package/dist/{chunk-JGMN6W72.js.map → chunk-2X4KUXYP.js.map} +0 -0
  104. /package/dist/{dom-to-image-NATF3446.js.map → chunk-BHHJOG5X.js.map} +0 -0
  105. /package/dist/{chunk-B4U37WIH.js.map → chunk-CFCLVJIO.js.map} +0 -0
  106. /package/dist/{chunk-PYDVANWI.js.map → chunk-E36VI2HA.js.map} +0 -0
  107. /package/dist/{chunk-PUVZ2S3F.js.map → chunk-G4NXVFXU.js.map} +0 -0
  108. /package/dist/{chunk-JAGNXEWK.js.map → chunk-GK4XW4BC.js.map} +0 -0
  109. /package/dist/{chunk-4VE6ZXXW.js.map → chunk-MQP57DOP.js.map} +0 -0
  110. /package/dist/{chunk-PGR2MDDU.js.map → chunk-W66OJM55.js.map} +0 -0
  111. /package/dist/{chunk-7OYIDM42.js.map → chunk-XKURJ72I.js.map} +0 -0
package/dist/blocks.cjs CHANGED
@@ -128,7 +128,9 @@ var init_colors = __esm({
128
128
  "red-200": "#FECACA",
129
129
  "red-400": "#F7776C",
130
130
  "red-500": "#EF4444",
131
- "red-600": "#DC2626"
131
+ "red-600": "#DC2626",
132
+ // Deck
133
+ "deck-canvas": "#faf8f5"
132
134
  };
133
135
  }
134
136
  });
@@ -271,8 +273,12 @@ __export(blocks_exports, {
271
273
  Block: () => Block,
272
274
  BlockContext: () => BlockContext,
273
275
  BlockServices: () => BlockServices,
276
+ NotificationDraftBlock: () => NotificationDraftBlock,
277
+ NotificationDraftBlockService: () => NotificationDraftBlockService,
274
278
  TextBlockService: () => TextBlockService,
275
279
  blockTypes: () => blockTypes,
280
+ notificationChannels: () => notificationChannels,
281
+ notificationEmailTypes: () => notificationEmailTypes,
276
282
  useBlockContext: () => useBlockContext
277
283
  });
278
284
  module.exports = __toCommonJS(blocks_exports);
@@ -280,13 +286,15 @@ module.exports = __toCommonJS(blocks_exports);
280
286
  // src/components/blocks/types.ts
281
287
  var blockTypes = {
282
288
  TEXT: "TEXT",
283
- ARTIFACT_REF: "ARTIFACT_REF"
289
+ ARTIFACT_REF: "ARTIFACT_REF",
290
+ NOTIFICATION_DRAFT: "NOTIFICATION_DRAFT"
284
291
  };
285
292
 
286
293
  // src/components/artifacts/artifact-base.ts
287
294
  var artifactTypes = {
288
295
  TABLE: "TABLE",
289
- CHART: "CHART"
296
+ CHART: "CHART",
297
+ DECK: "DECK"
290
298
  };
291
299
 
292
300
  // src/components/artifacts/chart-artifact/types.ts
@@ -311,6 +319,34 @@ var DEFAULT_TABLE_ARTIFACT_LABELS = {
311
319
  empty: "No rows to display."
312
320
  };
313
321
 
322
+ // src/components/artifacts/deck-artifact/types.ts
323
+ var deckBlockTypes = {
324
+ TEXT: "TEXT",
325
+ CHART: "CHART",
326
+ TABLE: "TABLE"
327
+ };
328
+ var deckSlideKinds = {
329
+ TITLE: "TITLE",
330
+ AGENDA: "AGENDA",
331
+ SECTION: "SECTION",
332
+ CONTENT: "CONTENT",
333
+ CLOSING: "CLOSING"
334
+ };
335
+ var DEFAULT_DECK_ARTIFACT_LABELS = {
336
+ previous: "Previous slide",
337
+ next: "Next slide",
338
+ position: "Slide {current} of {total}",
339
+ download: "Download",
340
+ unavailable: "Deck content is unavailable.",
341
+ enterFullscreen: "Fullscreen",
342
+ exitFullscreen: "Exit fullscreen",
343
+ thankYou: "Thank you",
344
+ questions: "Questions?",
345
+ agenda: "Agenda",
346
+ chart: DEFAULT_CHART_ARTIFACT_LABELS,
347
+ table: DEFAULT_TABLE_ARTIFACT_LABELS
348
+ };
349
+
314
350
  // src/components/artifacts/chart-artifact/chart-artifact.tsx
315
351
  var import_react13 = require("react");
316
352
 
@@ -403,29 +439,53 @@ var createReactComponent = (type, iconName, iconNamePascal, iconNode) => {
403
439
  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" }]];
404
440
  var IconAlertCircle = createReactComponent("outline", "alert-circle", "AlertCircle", __iconNode);
405
441
 
442
+ // node_modules/@tabler/icons-react/dist/esm/icons/IconArrowsMaximize.mjs
443
+ 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" }]];
444
+ var IconArrowsMaximize = createReactComponent("outline", "arrows-maximize", "ArrowsMaximize", __iconNode2);
445
+
446
+ // node_modules/@tabler/icons-react/dist/esm/icons/IconArrowsMinimize.mjs
447
+ 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" }]];
448
+ var IconArrowsMinimize = createReactComponent("outline", "arrows-minimize", "ArrowsMinimize", __iconNode3);
449
+
406
450
  // node_modules/@tabler/icons-react/dist/esm/icons/IconCheck.mjs
407
- var __iconNode2 = [["path", { "d": "M5 12l5 5l10 -10", "key": "svg-0" }]];
408
- var IconCheck = createReactComponent("outline", "check", "Check", __iconNode2);
451
+ var __iconNode4 = [["path", { "d": "M5 12l5 5l10 -10", "key": "svg-0" }]];
452
+ var IconCheck = createReactComponent("outline", "check", "Check", __iconNode4);
453
+
454
+ // node_modules/@tabler/icons-react/dist/esm/icons/IconChevronDown.mjs
455
+ var __iconNode5 = [["path", { "d": "M6 9l6 6l6 -6", "key": "svg-0" }]];
456
+ var IconChevronDown = createReactComponent("outline", "chevron-down", "ChevronDown", __iconNode5);
457
+
458
+ // node_modules/@tabler/icons-react/dist/esm/icons/IconChevronLeft.mjs
459
+ var __iconNode6 = [["path", { "d": "M15 6l-6 6l6 6", "key": "svg-0" }]];
460
+ var IconChevronLeft = createReactComponent("outline", "chevron-left", "ChevronLeft", __iconNode6);
461
+
462
+ // node_modules/@tabler/icons-react/dist/esm/icons/IconChevronRight.mjs
463
+ var __iconNode7 = [["path", { "d": "M9 6l6 6l-6 6", "key": "svg-0" }]];
464
+ var IconChevronRight = createReactComponent("outline", "chevron-right", "ChevronRight", __iconNode7);
409
465
 
410
466
  // node_modules/@tabler/icons-react/dist/esm/icons/IconCopy.mjs
411
- 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" }]];
412
- var IconCopy = createReactComponent("outline", "copy", "Copy", __iconNode3);
467
+ var __iconNode8 = [["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" }]];
468
+ var IconCopy = createReactComponent("outline", "copy", "Copy", __iconNode8);
413
469
 
414
470
  // node_modules/@tabler/icons-react/dist/esm/icons/IconDownload.mjs
415
- 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" }]];
416
- var IconDownload = createReactComponent("outline", "download", "Download", __iconNode4);
471
+ var __iconNode9 = [["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" }]];
472
+ var IconDownload = createReactComponent("outline", "download", "Download", __iconNode9);
417
473
 
418
474
  // node_modules/@tabler/icons-react/dist/esm/icons/IconFileSpreadsheet.mjs
419
- 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" }]];
420
- var IconFileSpreadsheet = createReactComponent("outline", "file-spreadsheet", "FileSpreadsheet", __iconNode5);
475
+ var __iconNode10 = [["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" }]];
476
+ var IconFileSpreadsheet = createReactComponent("outline", "file-spreadsheet", "FileSpreadsheet", __iconNode10);
421
477
 
422
478
  // node_modules/@tabler/icons-react/dist/esm/icons/IconLoader2.mjs
423
- var __iconNode6 = [["path", { "d": "M12 3a9 9 0 1 0 9 9", "key": "svg-0" }]];
424
- var IconLoader2 = createReactComponent("outline", "loader-2", "Loader2", __iconNode6);
479
+ var __iconNode11 = [["path", { "d": "M12 3a9 9 0 1 0 9 9", "key": "svg-0" }]];
480
+ var IconLoader2 = createReactComponent("outline", "loader-2", "Loader2", __iconNode11);
425
481
 
426
482
  // node_modules/@tabler/icons-react/dist/esm/icons/IconPhoto.mjs
427
- 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" }]];
428
- var IconPhoto = createReactComponent("outline", "photo", "Photo", __iconNode7);
483
+ var __iconNode12 = [["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" }]];
484
+ var IconPhoto = createReactComponent("outline", "photo", "Photo", __iconNode12);
485
+
486
+ // node_modules/@tabler/icons-react/dist/esm/icons/IconFlask2Filled.mjs
487
+ var __iconNode13 = [["path", { "d": "M15 2a1 1 0 0 1 0 2v5.674l.062 .03a7 7 0 0 1 3.85 5.174l.037 .262a7 7 0 0 1 -3.078 6.693a1 1 0 0 1 -.553 .167h-6.635a1 1 0 0 1 -.552 -.166a7 7 0 0 1 .807 -12.134l.062 -.028v-5.672a1 1 0 1 1 0 -2h6zm-2 2h-2v6.34a1 1 0 0 1 -.551 .894l-.116 .049a5 5 0 0 0 -2.92 2.717h9.172a5 5 0 0 0 -2.918 -2.715a1 1 0 0 1 -.667 -.943v-6.342z", "key": "svg-0" }]];
488
+ var IconFlask2Filled = createReactComponent("filled", "flask-2-filled", "Flask2Filled", __iconNode13);
429
489
 
430
490
  // src/components/artifacts/artifact-copy-as-image-button/artifact-copy-as-image-button.tsx
431
491
  var import_react6 = require("react");
@@ -1998,8 +2058,13 @@ var ChartArtifactService = {
1998
2058
  toClipboardText: chartArtifactToClipboardText
1999
2059
  };
2000
2060
 
2001
- // src/components/artifacts/table-artifact/table-artifact.tsx
2002
- var import_react15 = require("react");
2061
+ // src/components/artifacts/deck-artifact/deck-artifact.tsx
2062
+ var import_react17 = require("react");
2063
+ var import_react_dom = require("react-dom");
2064
+ init_capture_exclude_attribute();
2065
+
2066
+ // src/components/artifacts/deck-artifact/deck-artifact-content.tsx
2067
+ var import_react16 = require("react");
2003
2068
 
2004
2069
  // src/components/ui/table.tsx
2005
2070
  var React3 = __toESM(require("react"), 1);
@@ -2078,6 +2143,36 @@ function useScrollShadow() {
2078
2143
  return { targetRef, isScrolled };
2079
2144
  }
2080
2145
 
2146
+ // src/components/table/data-table.tsx
2147
+ var import_jsx_runtime21 = require("react/jsx-runtime");
2148
+ var DATA_TABLE_HEAD_CLASS = "h-auto py-2 px-4 text-xs font-medium text-foreground whitespace-nowrap text-left";
2149
+ var DATA_TABLE_CELL_CLASS = "p-4 text-sm font-normal text-foreground align-top min-w-[150px] text-left";
2150
+ var DATA_TABLE_ROW_CLASS = "border-b-brown-30 bg-background/70 hover:bg-secondary";
2151
+ var DATA_TABLE_SCROLL_SHADOW = "0 1px 2px rgb(0 0 0 / 0.01), 0 6px 12px -2px rgb(0 0 0 / 0.03)";
2152
+ function DataTable({ header, children, maxBodyHeight, className }) {
2153
+ const { targetRef, isScrolled } = useScrollShadow();
2154
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
2155
+ "div",
2156
+ {
2157
+ ref: targetRef,
2158
+ className: cn("overscroll-none", className),
2159
+ style: maxBodyHeight !== void 0 ? { maxHeight: maxBodyHeight, overflowY: "auto" } : void 0,
2160
+ children: /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(Table, { containerClassName: "overflow-visible", children: [
2161
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
2162
+ TableHeader,
2163
+ {
2164
+ "data-scrolled": isScrolled ? "true" : void 0,
2165
+ className: "sticky top-0 z-10 bg-background/70 backdrop-blur-lg transition-shadow",
2166
+ style: isScrolled ? { boxShadow: DATA_TABLE_SCROLL_SHADOW } : void 0,
2167
+ children: header
2168
+ }
2169
+ ),
2170
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TableBody, { children })
2171
+ ] })
2172
+ }
2173
+ );
2174
+ }
2175
+
2081
2176
  // src/components/artifacts/table-artifact/table-artifact-content.constants.ts
2082
2177
  var TABLE_STATUS = {
2083
2178
  LOADING: "loading",
@@ -2090,14 +2185,10 @@ var TABLE_STATUS = {
2090
2185
  function formatCell(row, column) {
2091
2186
  return row[column.key] ?? "";
2092
2187
  }
2093
- var HEAD_CLASS = "h-auto py-2 px-4 text-xs font-medium text-foreground whitespace-nowrap text-left";
2094
- var CELL_CLASS = "p-4 text-sm font-normal text-foreground align-top min-w-[150px] text-left";
2095
- var ROW_CLASSES = "border-b-brown-30 bg-background/70 hover:bg-secondary";
2096
- var DATA_ROW_CLASSES = `${ROW_CLASSES} animate-in fade-in duration-300`;
2097
- var SCROLL_SHADOW = "0 1px 2px rgb(0 0 0 / 0.01), 0 6px 12px -2px rgb(0 0 0 / 0.03)";
2188
+ var DATA_ROW_CLASSES = `${DATA_TABLE_ROW_CLASS} animate-in fade-in duration-300`;
2098
2189
 
2099
2190
  // src/components/artifacts/table-artifact/table-artifact-content.tsx
2100
- var import_jsx_runtime21 = require("react/jsx-runtime");
2191
+ var import_jsx_runtime22 = require("react/jsx-runtime");
2101
2192
  function TableArtifactContent({
2102
2193
  columns,
2103
2194
  data,
@@ -2107,48 +2198,367 @@ function TableArtifactContent({
2107
2198
  labels
2108
2199
  }) {
2109
2200
  if (data.status === TABLE_STATUS.ERROR) {
2110
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(StatusMessage, { $tone: "error", children: labels.loadError });
2201
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(StatusMessage, { $tone: "error", children: labels.loadError });
2111
2202
  }
2112
2203
  if (data.status === TABLE_STATUS.UNAVAILABLE) {
2113
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(StatusMessage, { $tone: "info", children: labels.unavailable });
2204
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(StatusMessage, { $tone: "info", children: labels.unavailable });
2114
2205
  }
2115
2206
  if (data.status === TABLE_STATUS.LOADING) {
2116
2207
  if (typeof knownRowCount === "number" && knownRowCount > 0) {
2117
2208
  const shimmerCount = Math.min(knownRowCount, maxShimmerRows);
2118
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ArtifactTable, { columns, maxBodyHeight, children: renderShimmerRows(columns, shimmerCount) });
2209
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ArtifactTable, { columns, maxBodyHeight, children: renderShimmerRows(columns, shimmerCount) });
2119
2210
  }
2120
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(StatusMessage, { $tone: "info", children: labels.loading });
2211
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(StatusMessage, { $tone: "info", children: labels.loading });
2121
2212
  }
2122
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ArtifactTable, { columns, maxBodyHeight, children: data.rows.length === 0 ? renderEmptyRow(columns.length, labels.empty) : renderDataRows(columns, data.rows) });
2213
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ArtifactTable, { columns, maxBodyHeight, children: data.rows.length === 0 ? renderEmptyRow(columns.length, labels.empty) : renderDataRows(columns, data.rows) });
2123
2214
  }
2124
2215
  function ArtifactTable({ columns, maxBodyHeight, children }) {
2125
- const { targetRef, isScrolled } = useScrollShadow();
2126
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { ref: targetRef, className: "overscroll-none", style: { maxHeight: maxBodyHeight, overflowY: "auto" }, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(Table, { containerClassName: "overflow-visible", children: [
2127
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
2128
- TableHeader,
2216
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
2217
+ DataTable,
2218
+ {
2219
+ maxBodyHeight,
2220
+ header: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(TableRow, { className: "hover:bg-transparent", children: columns.map((column) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(TableHead, { scope: "col", className: DATA_TABLE_HEAD_CLASS, children: column.label }, column.key)) }),
2221
+ children
2222
+ }
2223
+ );
2224
+ }
2225
+ function renderShimmerRows(columns, count) {
2226
+ return Array.from({ length: count }).map((_, index) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(TableRow, { className: DATA_TABLE_ROW_CLASS, "aria-hidden": "true", children: columns.map((column) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(TableCell, { className: DATA_TABLE_CELL_CLASS, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "block h-3 w-3/4 animate-pulse rounded bg-muted-foreground/20" }) }, column.key)) }, `shimmer-${index}`));
2227
+ }
2228
+ function renderDataRows(columns, rows) {
2229
+ return rows.map((row, index) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(TableRow, { className: DATA_ROW_CLASSES, children: columns.map((column) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(TableCell, { className: DATA_TABLE_CELL_CLASS, children: formatCell(row, column) }, column.key)) }, index));
2230
+ }
2231
+ function renderEmptyRow(colSpan, emptyMessage) {
2232
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(TableRow, { className: "hover:bg-transparent", children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(TableCell, { colSpan, className: "py-6 text-center text-sm text-muted-foreground", children: emptyMessage }) });
2233
+ }
2234
+
2235
+ // src/components/artifacts/deck-artifact/deck-markdown.tsx
2236
+ var import_react_markdown = __toESM(require("react-markdown"), 1);
2237
+ var import_remark_breaks = __toESM(require("remark-breaks"), 1);
2238
+ var import_remark_gfm = __toESM(require("remark-gfm"), 1);
2239
+ var import_jsx_runtime23 = require("react/jsx-runtime");
2240
+ function sanitize(text) {
2241
+ return text.split("\n").filter((line) => {
2242
+ const t = line.trim();
2243
+ return !(t.startsWith("<!-- table-title:") && t.endsWith("-->"));
2244
+ }).join("\n");
2245
+ }
2246
+ var MARKDOWN_COMPONENTS = {
2247
+ h1: ({ node: _node, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("h1", { className: "text-[30px] font-semibold leading-tight text-foreground mb-3", ...props }),
2248
+ h2: ({ node: _node, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("h2", { className: "text-[26px] font-semibold leading-tight text-foreground mb-2", ...props }),
2249
+ h3: ({ node: _node, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("h3", { className: "text-[24px] font-semibold leading-tight text-foreground mb-2", ...props }),
2250
+ h4: ({ node: _node, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("h4", { className: "text-[24px] font-semibold leading-tight text-foreground mb-2", ...props }),
2251
+ h5: ({ node: _node, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("h5", { className: "text-[24px] font-semibold leading-tight text-foreground mb-2", ...props }),
2252
+ h6: ({ node: _node, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("h6", { className: "text-[24px] font-semibold leading-tight text-foreground mb-2", ...props }),
2253
+ p: ({ node: _node, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("p", { className: "text-[22px] leading-relaxed text-foreground my-2", ...props }),
2254
+ ul: ({ node: _node, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("ul", { className: "list-disc pl-7 text-[22px] leading-relaxed text-foreground my-2", ...props }),
2255
+ ol: ({ node: _node, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("ol", { className: "list-decimal pl-7 text-[22px] leading-relaxed text-foreground my-2", ...props }),
2256
+ // react-markdown injects list metadata (`ordered` / `index` / `checked`) that must not leak to the DOM.
2257
+ li: ({ node: _node, ordered: _ordered, index: _index, checked: _checked, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("li", { className: "my-1", ...props }),
2258
+ strong: ({ node: _node, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("strong", { className: "font-semibold", ...props }),
2259
+ em: ({ node: _node, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("em", { className: "italic", ...props }),
2260
+ // Gate `<img src>` against an http(s) allowlist. react-markdown v8 percent-encodes the URL but
2261
+ // does NOT protocol-filter image src (unlike link href), so an LLM-authored
2262
+ // `![](javascript:…)` / `![](data:…)` or a tracking-pixel `![](http://…)` would otherwise render
2263
+ // with the raw URL. Unsafe src → render nothing.
2264
+ img: ({ node: _node, src, ...props }) => typeof src === "string" && isSafeDownloadUrl(src) ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("img", { src, className: "max-w-full", ...props }) : null,
2265
+ a: ({ node: _node, ...props }) => (
2266
+ // `rel` is forced AFTER `{...props}` so it can't be overridden — if a consumer ever adds
2267
+ // `linkTarget="_blank"`, LLM-authored links still can't reach `window.opener`.
2268
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("a", { className: "underline underline-offset-2", ...props, rel: "noopener noreferrer" })
2269
+ ),
2270
+ // react-markdown passes `inline` to distinguish inline code from fenced blocks — strip it from the DOM.
2271
+ code: ({ node: _node, inline: _inline, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("code", { className: "rounded bg-muted px-1.5 py-0.5 font-mono text-[18px]", ...props }),
2272
+ blockquote: ({ node: _node, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("blockquote", { className: "border-l-2 border-brown-alpha-12 pl-4 text-[22px] text-muted-foreground", ...props }),
2273
+ hr: ({ node: _node, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("hr", { className: "my-4 border-brown-alpha-12", ...props }),
2274
+ table: ({ node: _node, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "overflow-x-auto", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("table", { className: "w-full border-collapse text-[20px]", ...props }) }),
2275
+ // react-markdown passes `isHeader` to cell renderers — strip it so it never reaches the DOM.
2276
+ th: ({ node: _node, isHeader: _isHeader, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("th", { className: "border-b border-brown-alpha-12 px-3 py-1.5 text-left font-medium", ...props }),
2277
+ td: ({ node: _node, isHeader: _isHeader, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("td", { className: "border-b border-brown-alpha-12 px-3 py-1.5", ...props })
2278
+ };
2279
+ function DeckMarkdown({ markdown }) {
2280
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_react_markdown.default, { remarkPlugins: [import_remark_gfm.default, import_remark_breaks.default], components: MARKDOWN_COMPONENTS, children: sanitize(markdown) });
2281
+ }
2282
+
2283
+ // src/components/artifacts/deck-artifact/slide-renderers.tsx
2284
+ var import_react15 = require("react");
2285
+ var import_jsx_runtime24 = require("react/jsx-runtime");
2286
+ function safeLogoUrl(meta) {
2287
+ return meta?.logoUrl && isSafeDownloadUrl(meta.logoUrl) ? meta.logoUrl : null;
2288
+ }
2289
+ var DESIGN_W = 1280;
2290
+ var DESIGN_H = 720;
2291
+ var EYEBROW_H = 84;
2292
+ var CANVAS_LOGO_MAX_H = 48;
2293
+ var ACCENT_FONT = { fontFamily: `"${fontFamilies.CRIMSON_PRO}", serif`, fontStyle: "italic" };
2294
+ function SlideFrame({ meta, children, accent = false, bare = false }) {
2295
+ const frameRef = (0, import_react15.useRef)(null);
2296
+ const [scale, setScale] = (0, import_react15.useState)(null);
2297
+ (0, import_react15.useLayoutEffect)(() => {
2298
+ const el = frameRef.current;
2299
+ if (!el) return;
2300
+ const apply = (width) => {
2301
+ if (width > 0) setScale(width / DESIGN_W);
2302
+ };
2303
+ apply(el.clientWidth);
2304
+ const ro = new ResizeObserver((entries) => apply(entries[0]?.contentRect.width ?? 0));
2305
+ ro.observe(el);
2306
+ return () => ro.disconnect();
2307
+ }, []);
2308
+ const bg = accent ? "bg-indigo-600" : "bg-deck-canvas";
2309
+ const eyebrowColor = accent ? "text-white/70" : "text-brown-60";
2310
+ const borderColor = accent ? "border-white/20" : "border-brown-alpha-12";
2311
+ const logoSrc = safeLogoUrl(meta);
2312
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { ref: frameRef, className: `relative aspect-[16/9] w-full overflow-hidden rounded-lg ${bg}`, children: /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
2313
+ "div",
2314
+ {
2315
+ className: "absolute left-0 top-0 origin-top-left",
2316
+ style: {
2317
+ width: DESIGN_W,
2318
+ height: DESIGN_H,
2319
+ transform: `scale(${scale ?? 0})`,
2320
+ visibility: scale === null ? "hidden" : "visible"
2321
+ },
2322
+ children: [
2323
+ !bare ? /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
2324
+ "div",
2325
+ {
2326
+ className: `absolute inset-x-0 top-0 flex items-center justify-between border-b px-16 ${borderColor}`,
2327
+ style: { height: EYEBROW_H },
2328
+ children: [
2329
+ meta?.eyebrow ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { className: eyebrowColor, style: { ...ACCENT_FONT, fontSize: 22 }, children: meta.eyebrow }) : /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", {}),
2330
+ logoSrc ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("img", { src: logoSrc, alt: "", className: "object-contain", style: { maxHeight: CANVAS_LOGO_MAX_H } }) : null
2331
+ ]
2332
+ }
2333
+ ) : null,
2334
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "absolute inset-x-0 bottom-0 overflow-hidden", style: { top: bare ? 0 : EYEBROW_H }, children })
2335
+ ]
2336
+ }
2337
+ ) });
2338
+ }
2339
+ function TitleSlide({ slide, meta }) {
2340
+ const logoSrc = safeLogoUrl(meta);
2341
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(SlideFrame, { meta, bare: true, children: /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "flex h-full flex-col justify-center px-24 py-16", children: [
2342
+ logoSrc ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
2343
+ "img",
2129
2344
  {
2130
- "data-scrolled": isScrolled ? "true" : void 0,
2131
- className: "sticky top-0 z-10 bg-background/70 backdrop-blur-lg transition-shadow",
2132
- style: isScrolled ? { boxShadow: SCROLL_SHADOW } : void 0,
2133
- children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TableRow, { className: "hover:bg-transparent", children: columns.map((column) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TableHead, { scope: "col", className: HEAD_CLASS, children: column.label }, column.key)) })
2345
+ src: logoSrc,
2346
+ alt: "",
2347
+ className: "mb-16 object-contain object-left",
2348
+ style: { maxHeight: 56, maxWidth: 380 }
2349
+ }
2350
+ ) : null,
2351
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
2352
+ "h1",
2353
+ {
2354
+ className: "font-bold tracking-tight text-brown-100",
2355
+ style: { fontSize: 92, lineHeight: 1.04, maxWidth: 1060 },
2356
+ children: slide.title
2134
2357
  }
2135
2358
  ),
2136
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TableBody, { children })
2359
+ slide.subtitle ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("p", { className: "mt-7 text-brown-70", style: { ...ACCENT_FONT, fontSize: 34 }, children: slide.subtitle }) : null,
2360
+ slide.description ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("p", { className: "mt-4 leading-relaxed text-brown-70", style: { fontSize: 24, maxWidth: 880 }, children: slide.description }) : null,
2361
+ meta?.date ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("p", { className: "mt-10 text-brown-70", style: { ...ACCENT_FONT, fontSize: 30 }, children: meta.date }) : null
2137
2362
  ] }) });
2138
2363
  }
2139
- function renderShimmerRows(columns, count) {
2140
- return Array.from({ length: count }).map((_, index) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TableRow, { className: ROW_CLASSES, "aria-hidden": "true", children: columns.map((column) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TableCell, { className: CELL_CLASS, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "block h-3 w-3/4 animate-pulse rounded bg-muted-foreground/20" }) }, column.key)) }, `shimmer-${index}`));
2364
+ function AgendaSlide({ slide, meta, labels }) {
2365
+ const items = slide.items ?? [];
2366
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(SlideFrame, { meta, children: /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "h-full px-24 py-14", children: [
2367
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("h2", { className: "mb-10 font-bold text-brown-90", style: { fontSize: 40 }, children: slide.title || labels.agenda }),
2368
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "flex flex-col", children: items.map((item, i) => /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "flex items-baseline gap-6 border-b border-brown-alpha-12 py-5", children: [
2369
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { className: "shrink-0 text-indigo-600", style: { ...ACCENT_FONT, fontSize: 28, width: 64 }, children: String(i + 1).padStart(2, "0") }),
2370
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { className: "text-brown-90", style: { fontSize: 28 }, children: item })
2371
+ ] }, i)) })
2372
+ ] }) });
2141
2373
  }
2142
- function renderDataRows(columns, rows) {
2143
- return rows.map((row, index) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TableRow, { className: DATA_ROW_CLASSES, children: columns.map((column) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TableCell, { className: CELL_CLASS, children: formatCell(row, column) }, column.key)) }, index));
2374
+ function SectionSlide({ slide, meta }) {
2375
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(SlideFrame, { meta, accent: true, children: /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "flex h-full flex-col justify-center gap-6 px-24", children: [
2376
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("h2", { className: "font-bold text-white", style: { fontSize: 72, lineHeight: 1.05, maxWidth: 940 }, children: slide.title }),
2377
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "mt-2 bg-white/50", style: { width: 96, height: 5 } })
2378
+ ] }) });
2144
2379
  }
2145
- function renderEmptyRow(colSpan, emptyMessage) {
2146
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TableRow, { className: "hover:bg-transparent", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TableCell, { colSpan, className: "py-6 text-center text-sm text-muted-foreground", children: emptyMessage }) });
2380
+ function ClosingSlide({ meta, labels }) {
2381
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(SlideFrame, { meta, children: /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "flex h-full flex-col items-center justify-center gap-6 px-24 text-center", children: [
2382
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("h1", { className: "font-bold text-brown-100", style: { fontSize: 72 }, children: labels.thankYou }),
2383
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("p", { className: "text-indigo-600", style: { ...ACCENT_FONT, fontSize: 36 }, children: labels.questions })
2384
+ ] }) });
2147
2385
  }
2148
2386
 
2149
- // src/components/artifacts/table-artifact/use-table-artifact-data.ts
2150
- function useTableArtifactData(presignedUrl, columns) {
2151
- return useRowsArtifactData(presignedUrl, columns, parseCsv);
2387
+ // src/components/artifacts/deck-artifact/deck-artifact-content.tsx
2388
+ var import_jsx_runtime25 = require("react/jsx-runtime");
2389
+ var TABLE_BLOCK_MAX_SHIMMER_ROWS = 10;
2390
+ var TABLE_CHROME_RESERVE = 56;
2391
+ var VISUAL_FALLBACK_HEIGHT = 360;
2392
+ function DeckArtifactContent({
2393
+ slides,
2394
+ labels,
2395
+ onNavigate,
2396
+ meta,
2397
+ onDark = false,
2398
+ currentSlide,
2399
+ onSlideChange
2400
+ }) {
2401
+ const navBtnClass = onDark ? "[&_svg]:!text-white hover:!bg-white/20 disabled:opacity-40" : void 0;
2402
+ const navBtnSize = onDark ? "medium" : "small";
2403
+ const isControlled = currentSlide !== void 0;
2404
+ const [internalIndex, setInternalIndex] = (0, import_react16.useState)(0);
2405
+ const index = isControlled ? currentSlide : internalIndex;
2406
+ const current = Math.min(index, slides.length - 1);
2407
+ const slide = slides[current];
2408
+ const goTo = (0, import_react16.useCallback)(
2409
+ (next) => {
2410
+ const clamped = Math.max(0, Math.min(next, slides.length - 1));
2411
+ if (isControlled) onSlideChange?.(clamped);
2412
+ else setInternalIndex(clamped);
2413
+ onNavigate?.(clamped);
2414
+ },
2415
+ [slides.length, onNavigate, isControlled, onSlideChange]
2416
+ );
2417
+ const position = labels.position.replace("{current}", String(current + 1)).replace("{total}", String(slides.length));
2418
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "flex flex-col gap-3 p-4", children: [
2419
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(SlideDispatch, { slide, labels, meta }),
2420
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
2421
+ "div",
2422
+ {
2423
+ className: `flex items-center justify-between border-t pt-2 ${onDark ? "border-white/20" : "border-brown-alpha-12"}`,
2424
+ children: [
2425
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
2426
+ IconButton,
2427
+ {
2428
+ icon: IconChevronLeft,
2429
+ size: navBtnSize,
2430
+ className: navBtnClass,
2431
+ "aria-label": labels.previous,
2432
+ disabled: current === 0,
2433
+ onClick: () => goTo(current - 1)
2434
+ }
2435
+ ),
2436
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: `text-xs font-medium ${onDark ? "text-white/70" : "text-brown-70"}`, children: position }),
2437
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
2438
+ IconButton,
2439
+ {
2440
+ icon: IconChevronRight,
2441
+ size: navBtnSize,
2442
+ className: navBtnClass,
2443
+ "aria-label": labels.next,
2444
+ disabled: current === slides.length - 1,
2445
+ onClick: () => goTo(current + 1)
2446
+ }
2447
+ )
2448
+ ]
2449
+ }
2450
+ )
2451
+ ] });
2452
+ }
2453
+ function SlideDispatch({ slide, labels, meta }) {
2454
+ const kind = slide.kind ?? deckSlideKinds.CONTENT;
2455
+ switch (kind) {
2456
+ case deckSlideKinds.TITLE:
2457
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(TitleSlide, { slide, meta });
2458
+ case deckSlideKinds.AGENDA:
2459
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(AgendaSlide, { slide, meta, labels });
2460
+ case deckSlideKinds.SECTION:
2461
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(SectionSlide, { slide, meta });
2462
+ case deckSlideKinds.CLOSING:
2463
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(ClosingSlide, { meta, labels });
2464
+ case deckSlideKinds.CONTENT:
2465
+ default:
2466
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(ContentSlide, { slide, meta, labels });
2467
+ }
2468
+ }
2469
+ function isVisualBlock(block) {
2470
+ return block.type === deckBlockTypes.CHART || block.type === deckBlockTypes.TABLE;
2471
+ }
2472
+ function ContentSlide({ slide, meta, labels }) {
2473
+ const content = slide.content ?? [];
2474
+ const visuals = content.filter(isVisualBlock);
2475
+ const texts = content.filter(
2476
+ (b) => b.type === deckBlockTypes.TEXT
2477
+ );
2478
+ if (visuals.length === 1 && texts.length > 0) {
2479
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(SlideFrame, { meta, children: /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "flex h-full items-stretch gap-12 px-16 py-12", children: [
2480
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "flex w-[42%] shrink-0 flex-col overflow-hidden", children: /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(AutoFit, { children: [
2481
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("h3", { className: "mb-5 font-bold leading-tight text-foreground", style: { fontSize: 34 }, children: slide.title }),
2482
+ texts.map((block, i) => /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(DeckMarkdown, { markdown: block.markdown }, i))
2483
+ ] }) }),
2484
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "min-h-0 min-w-0 flex-1", children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(FillVisual, { block: visuals[0], labels }) })
2485
+ ] }) });
2486
+ }
2487
+ if (visuals.length > 0) {
2488
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(SlideFrame, { meta, children: /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "flex h-full flex-col gap-5 px-16 py-10", children: [
2489
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("h3", { className: "shrink-0 font-bold leading-tight text-foreground", style: { fontSize: 32 }, children: slide.title }),
2490
+ content.map(
2491
+ (block, i) => isVisualBlock(block) ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "min-h-0 min-w-0 flex-1", children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(FillVisual, { block, labels }) }, i) : /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "shrink-0 overflow-hidden", children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(DeckMarkdown, { markdown: block.markdown }) }, i)
2492
+ )
2493
+ ] }) });
2494
+ }
2495
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(SlideFrame, { meta, children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "h-full px-24 py-12", children: /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(AutoFit, { children: [
2496
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("h3", { className: "mb-6 font-bold leading-tight text-foreground", style: { fontSize: 38 }, children: slide.title }),
2497
+ texts.map((block, i) => /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(DeckMarkdown, { markdown: block.markdown }, i))
2498
+ ] }) }) });
2499
+ }
2500
+ function AutoFit({ children }) {
2501
+ const boxRef = (0, import_react16.useRef)(null);
2502
+ const contentRef = (0, import_react16.useRef)(null);
2503
+ const [fit, setFit] = (0, import_react16.useState)({ scale: 1, height: 0 });
2504
+ (0, import_react16.useLayoutEffect)(() => {
2505
+ const boxEl = boxRef.current;
2506
+ const contentEl = contentRef.current;
2507
+ if (!boxEl || !contentEl) return;
2508
+ const measure = () => {
2509
+ const avail = boxEl.clientHeight;
2510
+ const natural = contentEl.offsetHeight;
2511
+ const scale = avail > 0 && natural > avail ? avail / natural : 1;
2512
+ setFit({ scale, height: natural * scale });
2513
+ };
2514
+ measure();
2515
+ const ro = new ResizeObserver(measure);
2516
+ ro.observe(boxEl);
2517
+ ro.observe(contentEl);
2518
+ return () => ro.disconnect();
2519
+ }, []);
2520
+ const scaled = fit.scale < 1;
2521
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { ref: boxRef, className: "flex h-full min-h-0 w-full flex-col justify-center overflow-hidden", children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { style: scaled ? { height: fit.height } : void 0, children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
2522
+ "div",
2523
+ {
2524
+ ref: contentRef,
2525
+ className: "w-full origin-top-left",
2526
+ style: scaled ? { transform: `scale(${fit.scale})` } : void 0,
2527
+ children
2528
+ }
2529
+ ) }) });
2530
+ }
2531
+ function FillVisual({ block, labels }) {
2532
+ const ref = (0, import_react16.useRef)(null);
2533
+ const [height, setHeight] = (0, import_react16.useState)(VISUAL_FALLBACK_HEIGHT);
2534
+ (0, import_react16.useLayoutEffect)(() => {
2535
+ const el = ref.current;
2536
+ if (!el) return;
2537
+ const apply = (h) => {
2538
+ if (h > 0) setHeight(h);
2539
+ };
2540
+ apply(el.clientHeight);
2541
+ const ro = new ResizeObserver((entries) => apply(entries[0]?.contentRect.height ?? 0));
2542
+ ro.observe(el);
2543
+ return () => ro.disconnect();
2544
+ }, []);
2545
+ const chartLabels = labels.chart ?? DEFAULT_CHART_ARTIFACT_LABELS;
2546
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { ref, className: "h-full min-h-0 w-full overflow-hidden", children: block.type === deckBlockTypes.CHART ? (
2547
+ // ChartArtifactContent assumes a non-empty rowset (the standalone ChartArtifact guards
2548
+ // `rows.length > 0` upstream); mirror that guard here so an empty inline chart shows the
2549
+ // localized empty status instead of a blank chart area.
2550
+ block.rows.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(StatusMessage, { $tone: "info", children: chartLabels.empty }) : /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { style: { height }, children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(ChartArtifactContent, { config: block.config, rows: block.rows, labels: chartLabels }) })
2551
+ ) : /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
2552
+ TableArtifactContent,
2553
+ {
2554
+ columns: block.config.columns,
2555
+ data: { status: "ready", rows: block.rows, error: null },
2556
+ knownRowCount: block.config.stats?.rowCount ?? block.rows.length,
2557
+ maxShimmerRows: TABLE_BLOCK_MAX_SHIMMER_ROWS,
2558
+ maxBodyHeight: Math.max(0, height - TABLE_CHROME_RESERVE),
2559
+ labels: labels.table ?? DEFAULT_TABLE_ARTIFACT_LABELS
2560
+ }
2561
+ ) });
2152
2562
  }
2153
2563
 
2154
2564
  // src/components/artifacts/table-artifact/validate.ts
@@ -2167,19 +2577,265 @@ function isValidTableColumn(column) {
2167
2577
  return true;
2168
2578
  }
2169
2579
 
2170
- // src/components/artifacts/table-artifact/table-artifact.tsx
2171
- var import_jsx_runtime22 = require("react/jsx-runtime");
2580
+ // src/components/artifacts/deck-artifact/validate.ts
2581
+ function isValidDeckConfig(config) {
2582
+ if (!config || typeof config !== "object") return false;
2583
+ const candidate = config;
2584
+ if (!Array.isArray(candidate.slides)) return false;
2585
+ if (candidate.slides.length === 0) return false;
2586
+ return candidate.slides.every(isValidDeckSlide);
2587
+ }
2588
+ function isValidDeckSlide(slide) {
2589
+ if (!slide || typeof slide !== "object") return false;
2590
+ const candidate = slide;
2591
+ if (typeof candidate.title !== "string") return false;
2592
+ const kind = typeof candidate.kind === "string" ? candidate.kind : deckSlideKinds.CONTENT;
2593
+ if (kind === deckSlideKinds.CONTENT && !Array.isArray(candidate.content)) return false;
2594
+ const content = Array.isArray(candidate.content) ? candidate.content : [];
2595
+ if (candidate.items != null) {
2596
+ if (!Array.isArray(candidate.items)) return false;
2597
+ if (!candidate.items.every((item) => typeof item === "string")) return false;
2598
+ }
2599
+ return content.every(isValidDeckBlock);
2600
+ }
2601
+ function isValidDeckBlock(block) {
2602
+ if (!block || typeof block !== "object") return false;
2603
+ const candidate = block;
2604
+ switch (candidate.type) {
2605
+ case deckBlockTypes.TEXT:
2606
+ return typeof candidate.markdown === "string";
2607
+ case deckBlockTypes.CHART:
2608
+ return isValidChartConfig(candidate.config) && Array.isArray(candidate.rows);
2609
+ case deckBlockTypes.TABLE:
2610
+ return isValidTableConfig(candidate.config) && Array.isArray(candidate.rows);
2611
+ default:
2612
+ return false;
2613
+ }
2614
+ }
2615
+
2616
+ // src/components/artifacts/deck-artifact/deck-artifact.tsx
2617
+ var import_jsx_runtime26 = require("react/jsx-runtime");
2172
2618
  var TITLE_CLASSES2 = "text-sm font-semibold text-foreground";
2619
+ function DeckArtifact({ artifact, labels, callbacks }) {
2620
+ const validConfig = isValidDeckConfig(artifact.config);
2621
+ const [isFullscreen, setIsFullscreen] = (0, import_react17.useState)(false);
2622
+ const [currentSlide, setCurrentSlide] = (0, import_react17.useState)(0);
2623
+ (0, import_react17.useEffect)(() => {
2624
+ if (validConfig) return;
2625
+ console.warn("[clay] DeckArtifact: malformed `config` \u2014 skipping render.");
2626
+ }, [validConfig]);
2627
+ (0, import_react17.useEffect)(() => {
2628
+ if (!isFullscreen) return;
2629
+ const handleKey = (e) => {
2630
+ if (e.key === "Escape") setIsFullscreen(false);
2631
+ };
2632
+ document.addEventListener("keydown", handleKey);
2633
+ const previousOverflow = document.documentElement.style.overflow;
2634
+ document.documentElement.style.overflow = "hidden";
2635
+ return () => {
2636
+ document.removeEventListener("keydown", handleKey);
2637
+ document.documentElement.style.overflow = previousOverflow;
2638
+ };
2639
+ }, [isFullscreen]);
2640
+ const resolvedLabels = (0, import_react17.useMemo)(
2641
+ () => ({
2642
+ ...DEFAULT_DECK_ARTIFACT_LABELS,
2643
+ ...labels ?? {},
2644
+ chart: { ...DEFAULT_CHART_ARTIFACT_LABELS, ...labels?.chart ?? {} },
2645
+ table: { ...DEFAULT_TABLE_ARTIFACT_LABELS, ...labels?.table ?? {} }
2646
+ }),
2647
+ [labels]
2648
+ );
2649
+ const onNavigate = callbacks?.onNavigate;
2650
+ const handleNavigate = onNavigate ? (slideIndex) => onNavigate(artifact, slideIndex) : void 0;
2651
+ const onDownload = callbacks?.onDownload;
2652
+ const handleDownload = onDownload ? () => onDownload(artifact) : void 0;
2653
+ const presignedUrl = artifact.protectedAsset?.presignedUrl ?? null;
2654
+ const showDownload = presignedUrl !== null && isSafeDownloadUrl(presignedUrl);
2655
+ const meta = artifact.config?.meta;
2656
+ const handleOpenFullscreen = () => setIsFullscreen(true);
2657
+ const handleCloseFullscreen = () => setIsFullscreen(false);
2658
+ const actions = /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(ArtifactActions, { ...{ [CAPTURE_EXCLUDE_ATTRIBUTE]: "" }, children: [
2659
+ showDownload ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(ArtifactDownloadButton, { href: presignedUrl ?? "", label: resolvedLabels.download, onClick: handleDownload }) : null,
2660
+ validConfig ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
2661
+ IconButton,
2662
+ {
2663
+ icon: IconArrowsMaximize,
2664
+ size: "small",
2665
+ "aria-label": resolvedLabels.enterFullscreen,
2666
+ onClick: handleOpenFullscreen
2667
+ }
2668
+ ) : null
2669
+ ] });
2670
+ if (!validConfig) {
2671
+ return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(Artifact, { className: "shadow-none w-full rounded-2xl bg-background/70", children: [
2672
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(ArtifactHeader, { className: "bg-background/70", children: [
2673
+ artifact.title ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(ArtifactTitle, { className: TITLE_CLASSES2, children: artifact.title }) : /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", {}),
2674
+ actions
2675
+ ] }),
2676
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(ArtifactContent, { className: "p-0", children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(StatusMessage, { $tone: "info", children: resolvedLabels.unavailable }) })
2677
+ ] });
2678
+ }
2679
+ return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(import_jsx_runtime26.Fragment, { children: [
2680
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(Artifact, { className: "shadow-none w-full rounded-2xl bg-background/70", children: [
2681
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(ArtifactHeader, { className: "bg-background/70", children: [
2682
+ artifact.title ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(ArtifactTitle, { className: TITLE_CLASSES2, children: artifact.title }) : /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", {}),
2683
+ actions
2684
+ ] }),
2685
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(ArtifactContent, { className: "p-0", children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
2686
+ DeckArtifactContent,
2687
+ {
2688
+ slides: artifact.config.slides,
2689
+ labels: resolvedLabels,
2690
+ onNavigate: handleNavigate,
2691
+ meta,
2692
+ currentSlide,
2693
+ onSlideChange: setCurrentSlide
2694
+ }
2695
+ ) })
2696
+ ] }),
2697
+ isFullscreen && (0, import_react_dom.createPortal)(
2698
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
2699
+ FullscreenOverlay,
2700
+ {
2701
+ artifact,
2702
+ resolvedLabels,
2703
+ onNavigate: handleNavigate,
2704
+ currentSlide,
2705
+ onSlideChange: setCurrentSlide,
2706
+ onClose: handleCloseFullscreen
2707
+ }
2708
+ ),
2709
+ document.body
2710
+ )
2711
+ ] });
2712
+ }
2713
+ function FullscreenOverlay({
2714
+ artifact,
2715
+ resolvedLabels,
2716
+ onNavigate,
2717
+ currentSlide,
2718
+ onSlideChange,
2719
+ onClose
2720
+ }) {
2721
+ const meta = artifact.config?.meta;
2722
+ return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
2723
+ "div",
2724
+ {
2725
+ className: "fixed inset-0 z-[2147483647] flex items-center justify-center bg-black/80",
2726
+ role: "dialog",
2727
+ "aria-modal": "true",
2728
+ "aria-label": resolvedLabels.exitFullscreen,
2729
+ children: /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "relative", style: { width: "min(94vw, calc(82vh * 16 / 9))" }, children: [
2730
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
2731
+ DeckArtifactContent,
2732
+ {
2733
+ slides: artifact.config.slides,
2734
+ labels: resolvedLabels,
2735
+ onNavigate,
2736
+ meta,
2737
+ currentSlide,
2738
+ onSlideChange,
2739
+ onDark: true
2740
+ }
2741
+ ),
2742
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
2743
+ "button",
2744
+ {
2745
+ className: "absolute -top-10 right-6 flex items-center gap-1.5 text-sm text-white/80 hover:text-white",
2746
+ "aria-label": resolvedLabels.exitFullscreen,
2747
+ onClick: onClose,
2748
+ children: [
2749
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(IconArrowsMinimize, { size: 16 }),
2750
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { children: resolvedLabels.exitFullscreen })
2751
+ ]
2752
+ }
2753
+ )
2754
+ ] })
2755
+ }
2756
+ );
2757
+ }
2758
+
2759
+ // src/components/artifacts/deck-artifact/to-clipboard-text.ts
2760
+ function deckArtifactToClipboardText(artifact) {
2761
+ const header = artifact.title?.trim() || "Deck";
2762
+ const slides = artifact.config?.slides ?? [];
2763
+ const body = slides.map((slide, i) => slideToText(slide, i)).join("\n\n");
2764
+ return body ? `${header}
2765
+
2766
+ ${body}` : header;
2767
+ }
2768
+ function slideToText(slide, index) {
2769
+ const kind = slide.kind ?? deckSlideKinds.CONTENT;
2770
+ const prefix = `Slide ${index + 1}: `;
2771
+ switch (kind) {
2772
+ case deckSlideKinds.TITLE: {
2773
+ const parts = [slide.title];
2774
+ if (slide.subtitle) parts.push(slide.subtitle);
2775
+ if (slide.description) parts.push(slide.description);
2776
+ return `${prefix}${parts.join(" \u2014 ")}`.trim();
2777
+ }
2778
+ case deckSlideKinds.AGENDA: {
2779
+ const items = (slide.items ?? []).map((item, i) => ` ${String(i + 1).padStart(2, "0")}. ${item}`).join("\n");
2780
+ return `${prefix}${slide.title || "Agenda"}${items ? `
2781
+ ${items}` : ""}`.trim();
2782
+ }
2783
+ case deckSlideKinds.SECTION:
2784
+ return `${prefix}${slide.title}`.trim();
2785
+ case deckSlideKinds.CLOSING:
2786
+ return `${prefix}Thank you`.trim();
2787
+ case deckSlideKinds.CONTENT:
2788
+ default: {
2789
+ const blocks = (slide.content ?? []).map(blockToText).filter(Boolean).join("\n\n");
2790
+ return `${prefix}${slide.title}${blocks ? `
2791
+ ${blocks}` : ""}`.trim();
2792
+ }
2793
+ }
2794
+ }
2795
+ function blockToText(block) {
2796
+ switch (block.type) {
2797
+ case deckBlockTypes.TEXT:
2798
+ return block.markdown.trim();
2799
+ case deckBlockTypes.CHART:
2800
+ return `[Chart: ${block.config.variant}]`;
2801
+ case deckBlockTypes.TABLE: {
2802
+ const columnCount = block.config.columns.length;
2803
+ const rowCount = block.rows.length;
2804
+ return `[Table: ${columnCount} column${columnCount === 1 ? "" : "s"} \xD7 ${rowCount} row${rowCount === 1 ? "" : "s"}]`;
2805
+ }
2806
+ default:
2807
+ return "";
2808
+ }
2809
+ }
2810
+
2811
+ // src/components/artifacts/artifact-services/deck-artifact-service.ts
2812
+ var DeckArtifactService = {
2813
+ type: artifactTypes.DECK,
2814
+ Component: DeckArtifact,
2815
+ toClipboardText: deckArtifactToClipboardText
2816
+ };
2817
+
2818
+ // src/components/artifacts/table-artifact/table-artifact.tsx
2819
+ var import_react18 = require("react");
2820
+
2821
+ // src/components/artifacts/table-artifact/use-table-artifact-data.ts
2822
+ function useTableArtifactData(presignedUrl, columns) {
2823
+ return useRowsArtifactData(presignedUrl, columns, parseCsv);
2824
+ }
2825
+
2826
+ // src/components/artifacts/table-artifact/table-artifact.tsx
2827
+ var import_jsx_runtime27 = require("react/jsx-runtime");
2828
+ var TITLE_CLASSES3 = "text-sm font-semibold text-foreground";
2173
2829
  var TABLE_MAX_BODY_HEIGHT = 480;
2174
2830
  var MAX_SHIMMER_ROWS = 10;
2175
2831
  function TableArtifact({ artifact, labels, callbacks }) {
2176
2832
  const validConfig = isValidTableConfig(artifact.config);
2177
2833
  const validColumns = validConfig ? artifact.config.columns : null;
2178
- (0, import_react15.useEffect)(() => {
2834
+ (0, import_react18.useEffect)(() => {
2179
2835
  if (validConfig) return;
2180
2836
  console.warn("[clay] TableArtifact: malformed `config` \u2014 skipping render.");
2181
2837
  }, [validConfig]);
2182
- const resolvedLabels = (0, import_react15.useMemo)(
2838
+ const resolvedLabels = (0, import_react18.useMemo)(
2183
2839
  () => ({ ...DEFAULT_TABLE_ARTIFACT_LABELS, ...labels ?? {} }),
2184
2840
  [labels]
2185
2841
  );
@@ -2191,10 +2847,10 @@ function TableArtifact({ artifact, labels, callbacks }) {
2191
2847
  const onDownload = callbacks?.onDownload;
2192
2848
  const handleDownloadClick = onDownload ? () => onDownload(artifact) : void 0;
2193
2849
  if (!validConfig || !validColumns) {
2194
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(Artifact, { className: "shadow-none w-full rounded-2xl bg-background/70", children: [
2195
- /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(ArtifactHeader, { className: "bg-background/70", children: [
2196
- artifact.title ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ArtifactTitle, { className: TITLE_CLASSES2, children: artifact.title }) : /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", {}),
2197
- presignedUrl ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ArtifactActions, { children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
2850
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(Artifact, { className: "shadow-none w-full rounded-2xl bg-background/70", children: [
2851
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(ArtifactHeader, { className: "bg-background/70", children: [
2852
+ artifact.title ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(ArtifactTitle, { className: TITLE_CLASSES3, children: artifact.title }) : /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", {}),
2853
+ presignedUrl ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(ArtifactActions, { children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
2198
2854
  ArtifactDownloadButton,
2199
2855
  {
2200
2856
  href: presignedUrl,
@@ -2203,17 +2859,17 @@ function TableArtifact({ artifact, labels, callbacks }) {
2203
2859
  }
2204
2860
  ) }) : null
2205
2861
  ] }),
2206
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ArtifactContent, { className: "p-0 overflow-auto", style: { maxHeight: TABLE_MAX_BODY_HEIGHT }, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(StatusMessage, { $tone: "info", children: resolvedLabels.unavailable }) })
2862
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(ArtifactContent, { className: "p-0 overflow-auto", style: { maxHeight: TABLE_MAX_BODY_HEIGHT }, children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(StatusMessage, { $tone: "info", children: resolvedLabels.unavailable }) })
2207
2863
  ] });
2208
2864
  }
2209
2865
  return (
2210
2866
  // The table body owns its own scroll container (`ArtifactTable` inside `TableArtifactContent`).
2211
2867
  // Disable the outer overflow container so only one scroll context exists — this prevents
2212
2868
  // double-scroll nesting and lets `useScrollShadow` find the correct scroll ancestor.
2213
- /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(Artifact, { className: "shadow-none w-full rounded-2xl bg-background/70", children: [
2214
- /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(ArtifactHeader, { className: "bg-background/70", children: [
2215
- artifact.title ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ArtifactTitle, { className: TITLE_CLASSES2, children: artifact.title }) : /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", {}),
2216
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ArtifactActions, { children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
2869
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(Artifact, { className: "shadow-none w-full rounded-2xl bg-background/70", children: [
2870
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(ArtifactHeader, { className: "bg-background/70", children: [
2871
+ artifact.title ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(ArtifactTitle, { className: TITLE_CLASSES3, children: artifact.title }) : /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", {}),
2872
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(ArtifactActions, { children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
2217
2873
  ArtifactDownloadButton,
2218
2874
  {
2219
2875
  href: presignedUrl ?? "",
@@ -2222,7 +2878,7 @@ function TableArtifact({ artifact, labels, callbacks }) {
2222
2878
  }
2223
2879
  ) })
2224
2880
  ] }),
2225
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ArtifactContent, { className: "p-0 overflow-visible", style: { maxHeight: "none" }, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
2881
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(ArtifactContent, { className: "p-0 overflow-visible", style: { maxHeight: "none" }, children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
2226
2882
  TableArtifactContent,
2227
2883
  {
2228
2884
  columns: validColumns,
@@ -2253,7 +2909,8 @@ var TableArtifactService = {
2253
2909
  // src/components/artifacts/artifact-services/index.ts
2254
2910
  var ArtifactServices = {
2255
2911
  [artifactTypes.TABLE]: TableArtifactService,
2256
- [artifactTypes.CHART]: ChartArtifactService
2912
+ [artifactTypes.CHART]: ChartArtifactService,
2913
+ [artifactTypes.DECK]: DeckArtifactService
2257
2914
  };
2258
2915
  function artifactToClipboardText(artifact) {
2259
2916
  const entry = ArtifactServices[artifact.type];
@@ -2264,13 +2921,13 @@ function artifactToClipboardText(artifact) {
2264
2921
  }
2265
2922
 
2266
2923
  // src/components/blocks/artifact-ref-block/artifact-ref-block.tsx
2267
- var import_jsx_runtime23 = require("react/jsx-runtime");
2924
+ var import_jsx_runtime28 = require("react/jsx-runtime");
2268
2925
  function ArtifactRefBlock({ block }) {
2269
2926
  const { artifact, labels, callbacks } = block.payload;
2270
2927
  const entry = ArtifactServices[artifact.type];
2271
2928
  if (!entry) return null;
2272
2929
  const Component = entry.Component;
2273
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(Component, { artifact, labels, callbacks });
2930
+ return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(Component, { artifact, labels, callbacks });
2274
2931
  }
2275
2932
 
2276
2933
  // src/components/blocks/block-services/artifact-ref-block-service.ts
@@ -2285,29 +2942,102 @@ var ArtifactRefBlockService = {
2285
2942
  toClipboardText: (block) => artifactToClipboardText(block.payload.artifact)
2286
2943
  };
2287
2944
 
2288
- // src/utils/markdown-to-plain-text.ts
2289
- var PLACEHOLDER_PREFIX = "\u200B\u200BIC";
2290
- var PLACEHOLDER_SUFFIX = "\u200B\u200B";
2291
- var PLACEHOLDER_REGEX = new RegExp(`${PLACEHOLDER_PREFIX}(\\d+)${PLACEHOLDER_SUFFIX}`, "g");
2292
- function stripInlineMarks(text) {
2293
- return text.replace(/\*{3}(.+?)\*{3}/g, "$1").replace(/\*{2}(.+?)\*{2}/g, "$1").replace(/_{2}(.+?)_{2}/g, "$1").replace(/\*(.+?)\*/g, "$1").replace(/_(.+?)_/g, "$1").replace(/~~(.+?)~~/g, "$1");
2945
+ // src/components/blocks/notification-draft-block/notification-draft-block.tsx
2946
+ var import_react20 = require("react");
2947
+
2948
+ // src/components/badge/badge.tsx
2949
+ var import_jsx_runtime29 = require("react/jsx-runtime");
2950
+ var BASE = "inline-flex items-center whitespace-nowrap rounded-full";
2951
+ var SIZES = {
2952
+ // `sm` is a uniform scale-down of `md` (text 10/12 of caption): padding and line-height keep
2953
+ // md's proportions (caption is 12px text on a 16px line = 1.333 ratio) so the pill shape matches.
2954
+ md: "px-2.5 py-0.5 text-geist-label-caption-medium",
2955
+ sm: "px-2 py-0.5 text-[11px]/[1.333] font-medium"
2956
+ };
2957
+ var SUBTLE = {
2958
+ neutral: "bg-brown-20 text-brown-70",
2959
+ warning: "bg-orange-50 text-orange-600",
2960
+ violet: "bg-violet-50 text-violet-600"
2961
+ };
2962
+ var OUTLINE = {
2963
+ neutral: "border border-brown-30 bg-white text-brown-100",
2964
+ warning: "border border-orange-200 bg-white text-orange-600",
2965
+ violet: "border border-violet-400 bg-white text-violet-600"
2966
+ };
2967
+ var VARIANTS = {
2968
+ subtle: SUBTLE,
2969
+ outline: OUTLINE
2970
+ };
2971
+ function Badge({
2972
+ variant = "subtle",
2973
+ tone = "neutral",
2974
+ size = "md",
2975
+ className,
2976
+ children
2977
+ }) {
2978
+ return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { className: cn(BASE, SIZES[size], VARIANTS[variant][tone], className), children });
2294
2979
  }
2295
- function markdownToPlainText(markdown) {
2296
- if (!markdown) return "";
2297
- const inlineCodeSnippets = [];
2298
- let result = markdown.replace(/^<!--[\s\S]*?--!?>\n?/gm, "").replace(/```[\s\S]*?```/g, "").replace(/`([^`]+)`/g, (_match, code) => {
2299
- inlineCodeSnippets.push(code);
2300
- return `${PLACEHOLDER_PREFIX}${inlineCodeSnippets.length - 1}${PLACEHOLDER_SUFFIX}`;
2301
- }).replace(/^#{1,6}\s+/gm, "").replace(/^(?:---|\*\*\*|___)\s*$/gm, "");
2302
- result = stripInlineMarks(result).replace(/!\[([^\]]*)\]\([^)]+\)/g, "$1").replace(/\[([^\]]+)\]\([^)]+\)/g, "$1").replace(/^>\s?/gm, "").replace(/^\|[-:\s|]+\|\s*$/gm, "").replace(/^\|\s?/gm, "").replace(/\s?\|$/gm, "").replace(/\n{3,}/g, "\n\n").trim();
2303
- result = result.replace(PLACEHOLDER_REGEX, (_match, index) => inlineCodeSnippets[Number(index)]);
2304
- return result;
2980
+
2981
+ // src/components/chat/hooks/use-copy-to-clipboard.ts
2982
+ var import_react19 = require("react");
2983
+
2984
+ // src/utils/clipboard.ts
2985
+ function copyToClipboard(text, options) {
2986
+ navigator.clipboard.writeText(text).then(
2987
+ () => options?.onSuccess?.(),
2988
+ (error) => options?.onFailure?.(error)
2989
+ );
2305
2990
  }
2306
2991
 
2307
- // src/components/blocks/text-block/text-block.tsx
2308
- var import_react_markdown = __toESM(require("react-markdown"), 1);
2309
- var import_remark_breaks = __toESM(require("remark-breaks"), 1);
2310
- var import_remark_gfm = __toESM(require("remark-gfm"), 1);
2992
+ // src/components/chat/hooks/use-copy-to-clipboard.ts
2993
+ var RESET_DELAY_MS = 2e3;
2994
+ function useCopyToClipboard() {
2995
+ const [isCopied, setIsCopied] = (0, import_react19.useState)(false);
2996
+ const timeoutRef = (0, import_react19.useRef)(null);
2997
+ (0, import_react19.useEffect)(() => {
2998
+ return () => {
2999
+ if (timeoutRef.current) clearTimeout(timeoutRef.current);
3000
+ };
3001
+ }, []);
3002
+ const copy = (0, import_react19.useCallback)((text) => {
3003
+ copyToClipboard(text, {
3004
+ onSuccess: () => {
3005
+ setIsCopied(true);
3006
+ if (timeoutRef.current) clearTimeout(timeoutRef.current);
3007
+ timeoutRef.current = setTimeout(() => {
3008
+ setIsCopied(false);
3009
+ timeoutRef.current = null;
3010
+ }, RESET_DELAY_MS);
3011
+ }
3012
+ });
3013
+ }, []);
3014
+ return { isCopied, copy };
3015
+ }
3016
+
3017
+ // src/components/copy-button/copy-button.tsx
3018
+ var import_jsx_runtime30 = require("react/jsx-runtime");
3019
+ function CopyButton({
3020
+ text,
3021
+ copyLabel = "Copy",
3022
+ copiedLabel = "Copied",
3023
+ className
3024
+ }) {
3025
+ const { isCopied, copy } = useCopyToClipboard();
3026
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
3027
+ IconButton,
3028
+ {
3029
+ icon: isCopied ? IconCheck : IconCopy,
3030
+ onClick: () => copy(text),
3031
+ "aria-label": isCopied ? copiedLabel : copyLabel,
3032
+ className
3033
+ }
3034
+ );
3035
+ }
3036
+
3037
+ // src/components/blocks/markdown/markdown.tsx
3038
+ var import_react_markdown2 = __toESM(require("react-markdown"), 1);
3039
+ var import_remark_breaks2 = __toESM(require("remark-breaks"), 1);
3040
+ var import_remark_gfm2 = __toESM(require("remark-gfm"), 1);
2311
3041
 
2312
3042
  // src/components/blocks/scrollable-table/scrollable-table-styles.ts
2313
3043
  var import_styled_components3 = __toESM(require("styled-components"), 1);
@@ -2322,13 +3052,13 @@ var TableScroll = import_styled_components3.default.div`
2322
3052
  `;
2323
3053
 
2324
3054
  // src/components/blocks/scrollable-table/scrollable-table.tsx
2325
- var import_jsx_runtime24 = require("react/jsx-runtime");
3055
+ var import_jsx_runtime31 = require("react/jsx-runtime");
2326
3056
  function ScrollableTable({ node: _node, ...props }) {
2327
- return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(TableContainer, { children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(TableScroll, { children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("table", { ...props }) }) });
3057
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(TableContainer, { children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(TableScroll, { children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("table", { ...props }) }) });
2328
3058
  }
2329
3059
 
2330
3060
  // src/components/blocks/text-block/sanitize.ts
2331
- function sanitize(text) {
3061
+ function sanitize2(text) {
2332
3062
  return text.split("\n").filter((line) => {
2333
3063
  const t = line.trim();
2334
3064
  return !(t.startsWith("<!-- table-title:") && t.endsWith("-->"));
@@ -2467,13 +3197,157 @@ var TextBlockWrapper = import_styled_components4.default.div`
2467
3197
  }
2468
3198
  `;
2469
3199
 
2470
- // src/components/blocks/text-block/text-block.tsx
2471
- var import_jsx_runtime25 = require("react/jsx-runtime");
3200
+ // src/components/blocks/markdown/markdown.tsx
3201
+ var import_jsx_runtime32 = require("react/jsx-runtime");
2472
3202
  var markdownComponents = {
2473
- table: ((tableProps) => /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(ScrollableTable, { ...tableProps }))
3203
+ table: ((tableProps) => /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(ScrollableTable, { ...tableProps }))
2474
3204
  };
3205
+ function Markdown({ children }) {
3206
+ return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(TextBlockWrapper, { children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_react_markdown2.default, { remarkPlugins: [import_remark_gfm2.default, import_remark_breaks2.default], components: markdownComponents, children: sanitize2(children) }) });
3207
+ }
3208
+
3209
+ // src/utils/markdown-to-plain-text.ts
3210
+ var PLACEHOLDER_PREFIX = "\u200B\u200BIC";
3211
+ var PLACEHOLDER_SUFFIX = "\u200B\u200B";
3212
+ var PLACEHOLDER_REGEX = new RegExp(`${PLACEHOLDER_PREFIX}(\\d+)${PLACEHOLDER_SUFFIX}`, "g");
3213
+ function stripInlineMarks(text) {
3214
+ return text.replace(/\*{3}(.+?)\*{3}/g, "$1").replace(/\*{2}(.+?)\*{2}/g, "$1").replace(/_{2}(.+?)_{2}/g, "$1").replace(/\*(.+?)\*/g, "$1").replace(/_(.+?)_/g, "$1").replace(/~~(.+?)~~/g, "$1");
3215
+ }
3216
+ function markdownToPlainText(markdown) {
3217
+ if (!markdown) return "";
3218
+ const inlineCodeSnippets = [];
3219
+ let result = markdown.replace(/^<!--[\s\S]*?--!?>\n?/gm, "").replace(/```[\s\S]*?```/g, "").replace(/`([^`]+)`/g, (_match, code) => {
3220
+ inlineCodeSnippets.push(code);
3221
+ return `${PLACEHOLDER_PREFIX}${inlineCodeSnippets.length - 1}${PLACEHOLDER_SUFFIX}`;
3222
+ }).replace(/^#{1,6}\s+/gm, "").replace(/^(?:---|\*\*\*|___)\s*$/gm, "");
3223
+ result = stripInlineMarks(result).replace(/!\[([^\]]*)\]\([^)]+\)/g, "$1").replace(/\[([^\]]+)\]\([^)]+\)/g, "$1").replace(/^>\s?/gm, "").replace(/^\|[-:\s|]+\|\s*$/gm, "").replace(/^\|\s?/gm, "").replace(/\s?\|$/gm, "").replace(/\n{3,}/g, "\n\n").trim();
3224
+ result = result.replace(PLACEHOLDER_REGEX, (_match, index) => inlineCodeSnippets[Number(index)]);
3225
+ return result;
3226
+ }
3227
+
3228
+ // src/components/blocks/notification-draft-block/types.ts
3229
+ var notificationChannels = {
3230
+ /** Branded email — renders a From/subject header. */
3231
+ EMAIL: "email",
3232
+ /** Slack DM — renders as a chat bubble, no subject. */
3233
+ SLACK: "slack",
3234
+ /** Microsoft Teams DM — renders as a chat bubble, no subject. */
3235
+ TEAMS: "teams"
3236
+ };
3237
+ var notificationEmailTypes = {
3238
+ /** Recipient's work email. */
3239
+ WORK: "work",
3240
+ /** Recipient's personal email. */
3241
+ PERSONAL: "personal"
3242
+ };
3243
+
3244
+ // src/components/blocks/notification-draft-block/to-text.ts
3245
+ function notificationDraftToText(payload) {
3246
+ const { channel, subject, body } = payload;
3247
+ const text = markdownToPlainText(body);
3248
+ return channel === notificationChannels.EMAIL && subject ? `${subject}
3249
+
3250
+ ${text}` : text;
3251
+ }
3252
+
3253
+ // src/components/blocks/notification-draft-block/notification-draft-block.tsx
3254
+ var import_jsx_runtime33 = require("react/jsx-runtime");
3255
+ var CHIP_LIMIT = 3;
3256
+ var RECIPIENTS_MAX_HEIGHT = 320;
3257
+ function chipLabel(recipient) {
3258
+ return recipient.name ?? recipient.email ?? recipient.employeeId;
3259
+ }
3260
+ function channelCellText(recipient) {
3261
+ return recipient.fallback ? `${recipient.channelLabel} (fallback)` : recipient.channelLabel;
3262
+ }
3263
+ function FieldLabel({ children }) {
3264
+ return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { className: "text-geist-label-caption-medium tracking-wide text-brown-60", children });
3265
+ }
3266
+ function RecipientChips({ recipients }) {
3267
+ const shown = recipients.slice(0, CHIP_LIMIT);
3268
+ const overflow = recipients.length - shown.length;
3269
+ return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: "flex flex-wrap items-center gap-1.5", children: [
3270
+ shown.map((recipient) => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(Badge, { variant: "outline", children: chipLabel(recipient) }, recipient.employeeId)),
3271
+ overflow > 0 && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(Badge, { variant: "subtle", children: `+${overflow}` })
3272
+ ] });
3273
+ }
3274
+ function RecipientsTable({ recipients }) {
3275
+ return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
3276
+ DataTable,
3277
+ {
3278
+ maxBodyHeight: RECIPIENTS_MAX_HEIGHT,
3279
+ header: /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(TableRow, { className: "hover:bg-transparent", children: [
3280
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(TableHead, { scope: "col", className: DATA_TABLE_HEAD_CLASS, children: "Name" }),
3281
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(TableHead, { scope: "col", className: DATA_TABLE_HEAD_CLASS, children: "Email" }),
3282
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(TableHead, { scope: "col", className: DATA_TABLE_HEAD_CLASS, children: "Channel" })
3283
+ ] }),
3284
+ children: recipients.map((recipient) => /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(TableRow, { className: DATA_TABLE_ROW_CLASS, children: [
3285
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(TableCell, { className: DATA_TABLE_CELL_CLASS, children: recipient.name ?? "\u2014" }),
3286
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(TableCell, { className: DATA_TABLE_CELL_CLASS, children: recipient.email ?? "\u2014" }),
3287
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(TableCell, { className: DATA_TABLE_CELL_CLASS, children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(Badge, { tone: recipient.fallback ? "warning" : "neutral", variant: "subtle", children: channelCellText(recipient) }) })
3288
+ ] }, recipient.employeeId))
3289
+ }
3290
+ );
3291
+ }
3292
+ function NotificationDraftBlock({ block }) {
3293
+ const { channel, subject, body, recipients } = block.payload;
3294
+ const [detailsOpen, setDetailsOpen] = (0, import_react20.useState)(false);
3295
+ const showSubject = channel === notificationChannels.EMAIL && subject;
3296
+ return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: "overflow-hidden rounded-2xl border border-brown-30 bg-white", children: [
3297
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: "flex flex-col gap-3 border-b border-brown-30 px-4 py-3", children: [
3298
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: "flex items-center justify-between gap-2", children: [
3299
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: "flex items-center gap-2", children: [
3300
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { className: "text-geist-body-s-semi-bold text-brown-100", children: "Notification draft" }),
3301
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(Badge, { tone: "violet", variant: "subtle", size: "sm", className: "gap-1 py-1", children: [
3302
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(Icon, { icon: IconFlask2Filled, size: 12 }),
3303
+ "Labs"
3304
+ ] })
3305
+ ] }),
3306
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(CopyButton, { text: notificationDraftToText(block.payload), copyLabel: "Copy draft", copiedLabel: "Copied" })
3307
+ ] }),
3308
+ showSubject && /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: "flex flex-col gap-0.5", children: [
3309
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(FieldLabel, { children: "Title" }),
3310
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("p", { className: "text-geist-body-s-medium text-brown-100", children: subject })
3311
+ ] }),
3312
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: "flex flex-col gap-1", children: [
3313
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: "flex items-center justify-between gap-2", children: [
3314
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(FieldLabel, { children: `Recipients (${recipients.length})` }),
3315
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
3316
+ "button",
3317
+ {
3318
+ type: "button",
3319
+ "aria-expanded": detailsOpen,
3320
+ onClick: () => setDetailsOpen((open) => !open),
3321
+ className: "inline-flex items-center gap-1 text-geist-label-caption-medium text-brown-70 transition-colors hover:text-brown-100",
3322
+ children: [
3323
+ "Details",
3324
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(Icon, { icon: IconChevronDown, size: 14, className: detailsOpen ? "rotate-180" : void 0 })
3325
+ ]
3326
+ }
3327
+ )
3328
+ ] }),
3329
+ detailsOpen ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(RecipientsTable, { recipients }) : /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(RecipientChips, { recipients })
3330
+ ] })
3331
+ ] }),
3332
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "px-4 py-3", children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(Markdown, { children: body }) })
3333
+ ] });
3334
+ }
3335
+
3336
+ // src/components/blocks/block-services/notification-draft-block-service.ts
3337
+ var NotificationDraftBlockService = {
3338
+ type: blockTypes.NOTIFICATION_DRAFT,
3339
+ Component: NotificationDraftBlock,
3340
+ /**
3341
+ * Plain-text clipboard form of the drafted notification — shared with the in-block copy button
3342
+ * via {@link notificationDraftToText} so both copy the same text.
3343
+ */
3344
+ toClipboardText: (block) => notificationDraftToText(block.payload)
3345
+ };
3346
+
3347
+ // src/components/blocks/text-block/text-block.tsx
3348
+ var import_jsx_runtime34 = require("react/jsx-runtime");
2475
3349
  function TextBlock({ block }) {
2476
- return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(TextBlockWrapper, { children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_react_markdown.default, { remarkPlugins: [import_remark_gfm.default, import_remark_breaks.default], components: markdownComponents, children: sanitize(block.payload.text) }) });
3350
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(Markdown, { children: block.payload.text });
2477
3351
  }
2478
3352
 
2479
3353
  // src/components/blocks/block-services/text-block-service.ts
@@ -2491,23 +3365,24 @@ var TextBlockService = {
2491
3365
  // src/components/blocks/block-services/index.ts
2492
3366
  var BlockServices = {
2493
3367
  [blockTypes.TEXT]: TextBlockService,
2494
- [blockTypes.ARTIFACT_REF]: ArtifactRefBlockService
3368
+ [blockTypes.ARTIFACT_REF]: ArtifactRefBlockService,
3369
+ [blockTypes.NOTIFICATION_DRAFT]: NotificationDraftBlockService
2495
3370
  };
2496
3371
 
2497
3372
  // src/components/blocks/block/block.tsx
2498
- var import_jsx_runtime26 = require("react/jsx-runtime");
3373
+ var import_jsx_runtime35 = require("react/jsx-runtime");
2499
3374
  function Block({ block }) {
2500
3375
  const service = BlockServices[block.type];
2501
3376
  if (!service) return null;
2502
3377
  const { Component } = service;
2503
- return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(Component, { block });
3378
+ return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(Component, { block });
2504
3379
  }
2505
3380
 
2506
3381
  // src/components/blocks/block-context/block-context.tsx
2507
- var import_react16 = require("react");
2508
- var BlockContext = (0, import_react16.createContext)(null);
3382
+ var import_react21 = require("react");
3383
+ var BlockContext = (0, import_react21.createContext)(null);
2509
3384
  function useBlockContext() {
2510
- return (0, import_react16.useContext)(BlockContext) ?? {};
3385
+ return (0, import_react21.useContext)(BlockContext) ?? {};
2511
3386
  }
2512
3387
  // Annotate the CommonJS export names for ESM import in node:
2513
3388
  0 && (module.exports = {
@@ -2516,8 +3391,12 @@ function useBlockContext() {
2516
3391
  Block,
2517
3392
  BlockContext,
2518
3393
  BlockServices,
3394
+ NotificationDraftBlock,
3395
+ NotificationDraftBlockService,
2519
3396
  TextBlockService,
2520
3397
  blockTypes,
3398
+ notificationChannels,
3399
+ notificationEmailTypes,
2521
3400
  useBlockContext
2522
3401
  });
2523
3402
  /*! Bundled license information:
@@ -2525,12 +3404,18 @@ function useBlockContext() {
2525
3404
  @tabler/icons-react/dist/esm/defaultAttributes.mjs:
2526
3405
  @tabler/icons-react/dist/esm/createReactComponent.mjs:
2527
3406
  @tabler/icons-react/dist/esm/icons/IconAlertCircle.mjs:
3407
+ @tabler/icons-react/dist/esm/icons/IconArrowsMaximize.mjs:
3408
+ @tabler/icons-react/dist/esm/icons/IconArrowsMinimize.mjs:
2528
3409
  @tabler/icons-react/dist/esm/icons/IconCheck.mjs:
3410
+ @tabler/icons-react/dist/esm/icons/IconChevronDown.mjs:
3411
+ @tabler/icons-react/dist/esm/icons/IconChevronLeft.mjs:
3412
+ @tabler/icons-react/dist/esm/icons/IconChevronRight.mjs:
2529
3413
  @tabler/icons-react/dist/esm/icons/IconCopy.mjs:
2530
3414
  @tabler/icons-react/dist/esm/icons/IconDownload.mjs:
2531
3415
  @tabler/icons-react/dist/esm/icons/IconFileSpreadsheet.mjs:
2532
3416
  @tabler/icons-react/dist/esm/icons/IconLoader2.mjs:
2533
3417
  @tabler/icons-react/dist/esm/icons/IconPhoto.mjs:
3418
+ @tabler/icons-react/dist/esm/icons/IconFlask2Filled.mjs:
2534
3419
  @tabler/icons-react/dist/esm/tabler-icons-react.mjs:
2535
3420
  (**
2536
3421
  * @license @tabler/icons-react v3.37.1 - MIT