@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
@@ -16,6 +16,7 @@ import { C as ChartSeries } from './types-B2aYk82c.cjs';
16
16
  declare const artifactTypes: {
17
17
  readonly TABLE: "TABLE";
18
18
  readonly CHART: "CHART";
19
+ readonly DECK: "DECK";
19
20
  };
20
21
  /** Discriminator for artifact types. Additive — new types extend this union. */
21
22
  type ArtifactType = (typeof artifactTypes)[keyof typeof artifactTypes];
@@ -255,6 +256,201 @@ interface TableArtifactCallbacks {
255
256
  onDownload?: (artifact: TableArtifactRecord) => void;
256
257
  }
257
258
 
259
+ /**
260
+ * Block-type discriminator constants for `DeckBlock`. Uppercase to match the GraphQL
261
+ * `DeckBlockType` enum (which serializes as `TEXT` / `CHART` / `TABLE`) and the existing CHART
262
+ * `variant` precedent — the discriminator is identical end-to-end (agent → io-server → clay) with
263
+ * no case mapping at any boundary. Use these instead of string literals so a typo is a
264
+ * compile-time error rather than a silent wrong-branch at runtime.
265
+ */
266
+ declare const deckBlockTypes: {
267
+ readonly TEXT: "TEXT";
268
+ readonly CHART: "CHART";
269
+ readonly TABLE: "TABLE";
270
+ };
271
+ /**
272
+ * Slide-template kind discriminator constants for `DeckSlide`. Uppercase, end-to-end (agent →
273
+ * io-server → clay), mirroring `deckBlockTypes`. Use these instead of string literals so a typo
274
+ * is a compile-time error rather than a silent wrong-branch at runtime.
275
+ */
276
+ declare const deckSlideKinds: {
277
+ readonly TITLE: "TITLE";
278
+ readonly AGENDA: "AGENDA";
279
+ readonly SECTION: "SECTION";
280
+ readonly CONTENT: "CONTENT";
281
+ readonly CLOSING: "CLOSING";
282
+ };
283
+ /** Discriminator for the visual template a slide renders with. */
284
+ type DeckSlideKind = (typeof deckSlideKinds)[keyof typeof deckSlideKinds];
285
+ /**
286
+ * A markdown text block on a slide. Rendered with the same react-markdown pipeline as chat text
287
+ * blocks (GFM + single-newline breaks), so `**bold**`, lists, and headings render as formatted
288
+ * output rather than literal syntax.
289
+ */
290
+ interface DeckTextBlock {
291
+ /** Discriminator. */
292
+ type: typeof deckBlockTypes.TEXT;
293
+ /** Markdown source for the block body. */
294
+ markdown: string;
295
+ }
296
+ /**
297
+ * A chart block on a slide. Carries its data inline (`rows`) alongside the same `ChartConfig` the
298
+ * standalone `CHART` artifact uses — the deck renderer feeds both straight into
299
+ * `ChartArtifactContent` with no fetch (decks have no presigned data payload).
300
+ */
301
+ interface DeckChartBlock {
302
+ /** Discriminator. */
303
+ type: typeof deckBlockTypes.CHART;
304
+ /** Chart rendering config — reused verbatim from the `CHART` artifact. */
305
+ config: ChartConfig;
306
+ /** Inline data rows the chart plots. Each cell is a raw string (or `null`), matching the CSV wire. */
307
+ rows: ArtifactDataRow[];
308
+ }
309
+ /**
310
+ * A table block on a slide. Carries its data inline (`rows`) alongside the same `TableConfig` the
311
+ * standalone `TABLE` artifact uses — the deck renderer feeds both into `TableArtifactContent`
312
+ * (wrapping `rows` in a synthetic `"ready"` data state) with no fetch.
313
+ */
314
+ interface DeckTableBlock {
315
+ /** Discriminator. */
316
+ type: typeof deckBlockTypes.TABLE;
317
+ /** Table rendering config — reused verbatim from the `TABLE` artifact. */
318
+ config: TableConfig;
319
+ /** Inline data rows. Each cell is a raw string (or `null`), matching the CSV wire. */
320
+ rows: ArtifactDataRow[];
321
+ }
322
+ /**
323
+ * Discriminated union of the content blocks a slide can render. Discriminated by `type`; data is
324
+ * inline (no presigned-asset fetch). Older clay versions encountering a future block kind render
325
+ * it as the unavailable-state rather than crashing — see `DeckArtifactContent`.
326
+ */
327
+ type DeckBlock = DeckTextBlock | DeckChartBlock | DeckTableBlock;
328
+ /**
329
+ * A single slide inside a `DeckArtifact`. Unlike `TABLE` / `CHART`, a deck carries NO underlying
330
+ * CSV / protected-asset payload for rendering — its content lives entirely inline in
331
+ * `config.slides[*].content`, so the renderer reads slides straight from `artifact.config` and
332
+ * never fetches a presigned URL to draw them. (The presigned URL, when present, points at a
333
+ * downloadable `.pptx` export — a separate path from rendering.)
334
+ */
335
+ interface DeckSlide {
336
+ /** Slide heading, rendered prominently at the top of the slide. Required. */
337
+ title: string;
338
+ /** Ordered content blocks (text / chart / table) rendered top-to-bottom. May be empty. */
339
+ content: DeckBlock[];
340
+ /**
341
+ * Visual template kind. Absent / unknown → treated as `"CONTENT"` (backward-compatible).
342
+ * Use `deckSlideKinds` constants to avoid typos.
343
+ */
344
+ kind?: DeckSlideKind;
345
+ /** Subtitle line shown below the main title. Used on TITLE slides; ignored by other kinds. */
346
+ subtitle?: string;
347
+ /** Short description paragraph. Used on TITLE slides below the subtitle. */
348
+ description?: string;
349
+ /**
350
+ * Agenda items (AGENDA kind). Each string is one line item rendered with an ordinal
351
+ * prefix (`01`, `02`, …) in Crimson Pro italic accent. Ignored by non-AGENDA kinds.
352
+ */
353
+ items?: string[];
354
+ }
355
+ /**
356
+ * Optional stats embedded in `DeckConfig`. Mirrors `TableStats` / `ChartStats` — lets a consumer
357
+ * render deck chrome (e.g. "Slide 1 / N") before deeply inspecting the slide array.
358
+ *
359
+ * Backward-compat: optional everywhere; the renderer derives the count from `slides.length` when
360
+ * absent.
361
+ */
362
+ interface DeckStats {
363
+ /** Total number of slides in the deck. */
364
+ slideCount: number;
365
+ }
366
+ /**
367
+ * Optional deck-level metadata embedded in `DeckConfig`. Injected by the client at render time;
368
+ * clay renders each field if present and ignores it when absent.
369
+ */
370
+ interface DeckMeta {
371
+ /** Short eyebrow / running header text shown at top-left of every slide (e.g. org name). */
372
+ eyebrow?: string;
373
+ /** Date string shown on the TITLE slide (e.g. "Q2 2026"). No formatting is applied — rendered as-is. */
374
+ date?: string;
375
+ /**
376
+ * Theme identifier. Defaults to `"shapes-light"` when absent.
377
+ * Currently only `"shapes-light"` is implemented; unknown values fall back to the default.
378
+ */
379
+ theme?: string;
380
+ /**
381
+ * Account logo URL injected by the client at render time. Clay renders `<img src={logoUrl}>`
382
+ * (capped height, object-contain) at top-right of every slide when present. Omitted → no logo.
383
+ *
384
+ * CROSS-REPO DEVIATION: this field exists ONLY on clay's `DeckMeta`. io-server's `DeckMeta`
385
+ * (and the GraphQL SDL) deliberately omit `logoUrl` — it is client-injected from the account's
386
+ * branding and must NOT round-trip through the server. Keep the two `DeckMeta` shapes in sync on
387
+ * every field EXCEPT this one. The renderer validates it against an http(s) allowlist before use.
388
+ */
389
+ logoUrl?: string;
390
+ }
391
+ /** Rendering config for a deck artifact, stored at `artifact.config` (JSONB on the server). */
392
+ interface DeckConfig {
393
+ /** Ordered slides. At least one; the renderer guards against an empty array. */
394
+ slides: DeckSlide[];
395
+ /** Optional metadata describing the deck — used for chrome before slide inspection. */
396
+ stats?: DeckStats;
397
+ /** Optional deck-level metadata (eyebrow, date, theme, logo). */
398
+ meta?: DeckMeta;
399
+ }
400
+ /** Concrete deck artifact record. */
401
+ type DeckArtifactRecord = ArtifactBase<typeof artifactTypes.DECK, DeckConfig>;
402
+ /**
403
+ * Localized labels for the `DECK` artifact's user-facing strings. Covers navigation controls,
404
+ * the download action, and status messages. Consumer passes the bag from their i18n layer; clay
405
+ * falls back to English defaults when omitted (per-key Partial merge).
406
+ *
407
+ * `chart` / `table` are nested bags forwarded to embedded chart / table blocks' renderers so
408
+ * those inherit the consumer's translations (status / empty / loading strings). Both are optional;
409
+ * the orchestrator defaults them to `DEFAULT_CHART_ARTIFACT_LABELS` / `DEFAULT_TABLE_ARTIFACT_LABELS`.
410
+ */
411
+ interface DeckArtifactLabels {
412
+ /** aria-label on the "previous slide" navigation button. */
413
+ previous: string;
414
+ /** aria-label on the "next slide" navigation button. */
415
+ next: string;
416
+ /**
417
+ * Slide-position indicator template. `{current}` and `{total}` are substituted at render time
418
+ * (e.g. "Slide {current} of {total}" → "Slide 2 of 5").
419
+ */
420
+ position: string;
421
+ /** Tooltip + aria-label on the Download (`.pptx`) button in the deck artifact header. */
422
+ download: string;
423
+ /** Status message shown when the deck config is malformed or has no slides. */
424
+ unavailable: string;
425
+ /** aria-label on the expand / enter-fullscreen button in the artifact header. */
426
+ enterFullscreen: string;
427
+ /** aria-label on the collapse / exit-fullscreen button in the fullscreen overlay. */
428
+ exitFullscreen: string;
429
+ /** "Thank you" closing message shown on the CLOSING slide. */
430
+ thankYou: string;
431
+ /** "Questions?" prompt shown on the CLOSING slide below the thank-you message. */
432
+ questions: string;
433
+ /** Fallback title for the AGENDA slide when the slide's `title` is empty. */
434
+ agenda: string;
435
+ /** Label bag forwarded to embedded chart blocks. Defaults to `DEFAULT_CHART_ARTIFACT_LABELS`. */
436
+ chart?: ChartArtifactLabels;
437
+ /** Label bag forwarded to embedded table blocks. Defaults to `DEFAULT_TABLE_ARTIFACT_LABELS`. */
438
+ table?: TableArtifactLabels;
439
+ }
440
+ /** English fallback for `DeckArtifactLabels`. Used when the consumer hasn't passed `labels`. */
441
+ declare const DEFAULT_DECK_ARTIFACT_LABELS: DeckArtifactLabels;
442
+ /**
443
+ * Consumer-supplied side-effect callbacks for actions on the deck artifact. Mirrors
444
+ * `TableArtifactCallbacks` — every callback is optional and receives the `DeckArtifactRecord` so
445
+ * consumers can derive analytics context without a per-block closure at every render site.
446
+ */
447
+ interface DeckArtifactCallbacks {
448
+ /** Fires when the user navigates to a different slide. `slideIndex` is zero-based. */
449
+ onNavigate?: (artifact: DeckArtifactRecord, slideIndex: number) => void;
450
+ /** Fires when the Download (`.pptx`) button is clicked. Browser-native download still happens. */
451
+ onDownload?: (artifact: DeckArtifactRecord) => void;
452
+ }
453
+
258
454
  /**
259
455
  * Artifact-domain types — wire-protocol shapes shared between artifact components, block-services,
260
456
  * and consumers. Splits responsibilities across three layers so each artifact kind owns its own
@@ -278,16 +474,16 @@ interface TableArtifactCallbacks {
278
474
  * clay versions that don't know about a new type silently render nothing — see `ArtifactRefBlock`
279
475
  * guard.
280
476
  */
281
- type ArtifactRecord = TableArtifactRecord | ChartArtifactRecord;
477
+ type ArtifactRecord = TableArtifactRecord | ChartArtifactRecord | DeckArtifactRecord;
282
478
  /**
283
479
  * Type-level lookup from an `ArtifactRecord` branch to its labels / callbacks slice. The block
284
480
  * layer already carries the discriminator on `artifact.type`, so the renderer-side `labels` and
285
481
  * `callbacks` props expose exactly one type's slice — never a union-bag keyed by type. Consumers
286
482
  * emit a flat object; the dispatcher narrows it implicitly via `TArtifact`.
287
483
  */
288
- type ArtifactLabelsOf<TArtifact extends ArtifactRecord> = TArtifact extends ChartArtifactRecord ? ChartArtifactLabels : TArtifact extends TableArtifactRecord ? TableArtifactLabels : never;
484
+ type ArtifactLabelsOf<TArtifact extends ArtifactRecord> = TArtifact extends ChartArtifactRecord ? ChartArtifactLabels : TArtifact extends TableArtifactRecord ? TableArtifactLabels : TArtifact extends DeckArtifactRecord ? DeckArtifactLabels : never;
289
485
  /** Sibling to `ArtifactLabelsOf` — callbacks slice matching the artifact branch. */
290
- type ArtifactCallbacksOf<TArtifact extends ArtifactRecord> = TArtifact extends ChartArtifactRecord ? ChartArtifactCallbacks : TArtifact extends TableArtifactRecord ? TableArtifactCallbacks : never;
486
+ type ArtifactCallbacksOf<TArtifact extends ArtifactRecord> = TArtifact extends ChartArtifactRecord ? ChartArtifactCallbacks : TArtifact extends TableArtifactRecord ? TableArtifactCallbacks : TArtifact extends DeckArtifactRecord ? DeckArtifactCallbacks : never;
291
487
  /**
292
488
  * Props every concrete artifact component receives from the dispatcher.
293
489
  *
@@ -327,4 +523,4 @@ interface ArtifactService<TArtifact extends ArtifactRecord = ArtifactRecord> {
327
523
  toClipboardText: (artifact: TArtifact) => string;
328
524
  }
329
525
 
330
- export { type ArtifactCallbacksOf as A, type BarChartConfig as B, type ChartArtifactLabels as C, DEFAULT_CHART_ARTIFACT_LABELS as D, type LineChartConfig as L, type PieChartConfig as P, type TableArtifactLabels as T, type ArtifactDataRow as a, type ArtifactLabelsOf as b, type ArtifactRecord as c, type ArtifactService as d, type ArtifactServiceProps as e, type ArtifactType as f, type ChartArtifactRecord as g, type ChartConfig as h, type ChartStats as i, DEFAULT_TABLE_ARTIFACT_LABELS as j, type TableArtifactRecord as k, type TableColumn as l, type TableConfig as m, type TableRow as n, type TableStats as o, artifactTypes as p };
526
+ export { type ArtifactCallbacksOf as A, type BarChartConfig as B, type ChartArtifactLabels as C, DEFAULT_CHART_ARTIFACT_LABELS as D, type TableRow as E, type TableStats as F, artifactTypes as G, deckBlockTypes as H, deckSlideKinds as I, type LineChartConfig as L, type PieChartConfig as P, type TableArtifactLabels as T, type ArtifactDataRow as a, type ArtifactLabelsOf as b, type ArtifactRecord as c, type ArtifactService as d, type ArtifactServiceProps as e, type ArtifactType as f, type ChartArtifactRecord as g, type ChartConfig as h, type ChartStats as i, DEFAULT_DECK_ARTIFACT_LABELS as j, DEFAULT_TABLE_ARTIFACT_LABELS as k, type DeckArtifactCallbacks as l, type DeckArtifactLabels as m, type DeckArtifactRecord as n, type DeckBlock as o, type DeckChartBlock as p, type DeckConfig as q, type DeckMeta as r, type DeckSlide as s, type DeckSlideKind as t, type DeckStats as u, type DeckTableBlock as v, type DeckTextBlock as w, type TableArtifactRecord as x, type TableColumn as y, type TableConfig as z };
@@ -1,10 +1,82 @@
1
1
  import { ComponentType } from 'react';
2
- import { c as ArtifactRecord, b as ArtifactLabelsOf, A as ArtifactCallbacksOf } from './types-GCkXbyFm.js';
2
+ import { c as ArtifactRecord, b as ArtifactLabelsOf, A as ArtifactCallbacksOf } from './types-C8JtnwNY.js';
3
+
4
+ /** Delivery channel the drafted notification targets. */
5
+ declare const notificationChannels: {
6
+ /** Branded email — renders a From/subject header. */
7
+ readonly EMAIL: "email";
8
+ /** Slack DM — renders as a chat bubble, no subject. */
9
+ readonly SLACK: "slack";
10
+ /** Microsoft Teams DM — renders as a chat bubble, no subject. */
11
+ readonly TEAMS: "teams";
12
+ };
13
+ /** One of the supported notification channels. */
14
+ type NotificationChannel = (typeof notificationChannels)[keyof typeof notificationChannels];
15
+ /** Which recipient address the email is sent to (email channel only). */
16
+ declare const notificationEmailTypes: {
17
+ /** Recipient's work email. */
18
+ readonly WORK: "work";
19
+ /** Recipient's personal email. */
20
+ readonly PERSONAL: "personal";
21
+ };
22
+ /** One of the supported email address types. */
23
+ type NotificationEmailType = (typeof notificationEmailTypes)[keyof typeof notificationEmailTypes];
24
+ /**
25
+ * A single resolved recipient of the drafted notification — the block's one source of recipient
26
+ * truth. The summary chips and the detail table are both projected from this (count, chip labels,
27
+ * and fallback count are all derived, never transmitted separately).
28
+ */
29
+ interface NotificationRecipient {
30
+ /** Stable employee identifier — used as the React key. */
31
+ employeeId: string;
32
+ /** Display name; falls back to `email` for the chip label when absent. */
33
+ name?: string | null;
34
+ /** Resolved delivery address (work or personal, already resolved server-side). */
35
+ email?: string | null;
36
+ /**
37
+ * Human-readable label for the channel this recipient is reached on (e.g. "Email", "Slack").
38
+ * Client-provided so clay isn't limited to a fixed channel set and holds no channel copy of its
39
+ * own — the consumer passes whatever label fits its use case in the payload.
40
+ */
41
+ channelLabel: string;
42
+ /** Whether this recipient is unreachable on the chosen channel and falls back to email. */
43
+ fallback: boolean;
44
+ }
45
+ /**
46
+ * Wire payload for a drafted notification preview — populated by the agent from the
47
+ * `validate_send_notification` dry-run. Presentation only: this block previews what will be
48
+ * sent, it is NOT the approval/confirmation control.
49
+ */
50
+ interface NotificationDraftPayload {
51
+ /** Delivery channel; drives whether a subject title is shown (email only). */
52
+ channel: NotificationChannel;
53
+ /** Which address the email is sent to. Email channel only; ignored otherwise. */
54
+ emailType?: NotificationEmailType;
55
+ /** Email subject line. Email channel only; absent for slack/teams. */
56
+ subject?: string;
57
+ /** Message body (markdown source). Rendered as markdown, identical to chat assistant text. */
58
+ body: string;
59
+ /** Resolved From display name, e.g. "Acme HR via Shapes". Email channel only. */
60
+ fromDisplay?: string;
61
+ /**
62
+ * Every resolved recipient. Clay derives the count, the summary chips (`name ?? email`, with a
63
+ * "+N" overflow), and the fallback count from this single array.
64
+ */
65
+ recipients: NotificationRecipient[];
66
+ }
67
+ /** A NOTIFICATION_DRAFT content block — wraps a {@link NotificationDraftPayload}. */
68
+ interface NotificationDraftBlockRecord {
69
+ /** Block-type discriminator. */
70
+ type: typeof blockTypes.NOTIFICATION_DRAFT;
71
+ /** Type-specific data; kept as `payload` to match the wire shape 1:1. */
72
+ payload: NotificationDraftPayload;
73
+ }
3
74
 
4
75
  /** Block-type discriminator constants. Mirrors the wire shape from io-server / shapes-agent. */
5
76
  declare const blockTypes: {
6
77
  readonly TEXT: "TEXT";
7
78
  readonly ARTIFACT_REF: "ARTIFACT_REF";
79
+ readonly NOTIFICATION_DRAFT: "NOTIFICATION_DRAFT";
8
80
  };
9
81
  /** Discriminator for content blocks. */
10
82
  type BlockType = (typeof blockTypes)[keyof typeof blockTypes];
@@ -65,7 +137,7 @@ type ArtifactRefBlockRecord = ArtifactRecord extends infer TArtifact ? TArtifact
65
137
  };
66
138
  } : never : never;
67
139
  /** Discriminated union of all supported content blocks. Additive — new types extend this union. */
68
- type BlockRecord = TextBlockRecord | ArtifactRefBlockRecord;
140
+ type BlockRecord = TextBlockRecord | ArtifactRefBlockRecord | NotificationDraftBlockRecord;
69
141
  /** Props every concrete block component receives from the dispatcher. */
70
142
  interface BlockComponentProps<TBlock extends BlockRecord = BlockRecord> {
71
143
  /** The block to render. */
@@ -92,4 +164,4 @@ interface BlockService<TBlock extends BlockRecord = BlockRecord> {
92
164
  toClipboardText?: (block: TBlock) => string;
93
165
  }
94
166
 
95
- export { type ArtifactRefBlockRecord as A, type BlockComponentProps as B, type TextBlockRecord as T, type BlockRecord as a, type BlockService as b, type BlockType as c, blockTypes as d };
167
+ export { type ArtifactRefBlockRecord as A, type BlockComponentProps as B, type NotificationChannel as N, type TextBlockRecord as T, type BlockRecord as a, type BlockService as b, type BlockType as c, type NotificationDraftBlockRecord as d, type NotificationDraftPayload as e, type NotificationEmailType as f, type NotificationRecipient as g, blockTypes as h, notificationEmailTypes as i, notificationChannels as n };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shapesos/clay",
3
- "version": "0.17.0",
3
+ "version": "0.19.0",
4
4
  "main": "./dist/index.cjs",
5
5
  "module": "./dist/index.js",
6
6
  "devDependencies": {
@@ -133,6 +133,16 @@
133
133
  "types": "./dist/button.d.ts",
134
134
  "import": "./dist/button.js",
135
135
  "require": "./dist/button.cjs"
136
+ },
137
+ "./badge": {
138
+ "types": "./dist/badge.d.ts",
139
+ "import": "./dist/badge.js",
140
+ "require": "./dist/badge.cjs"
141
+ },
142
+ "./copy-button": {
143
+ "types": "./dist/copy-button.d.ts",
144
+ "import": "./dist/copy-button.js",
145
+ "require": "./dist/copy-button.cjs"
136
146
  }
137
147
  },
138
148
  "description": "Shapes.co design system — tokens and theming",
@@ -1,250 +0,0 @@
1
- import {
2
- markdownToPlainText
3
- } from "./chunk-NMKKU2UG.js";
4
- import {
5
- ArtifactServices,
6
- artifactToClipboardText
7
- } from "./chunk-6YZ65KPW.js";
8
- import {
9
- typographyMixin,
10
- typographyTypes
11
- } from "./chunk-UXT2H3FM.js";
12
- import {
13
- colors
14
- } from "./chunk-JF3P66JF.js";
15
-
16
- // src/components/blocks/types.ts
17
- var blockTypes = {
18
- TEXT: "TEXT",
19
- ARTIFACT_REF: "ARTIFACT_REF"
20
- };
21
-
22
- // src/components/blocks/artifact-ref-block/artifact-ref-block.tsx
23
- import { jsx } from "react/jsx-runtime";
24
- function ArtifactRefBlock({ block }) {
25
- const { artifact, labels, callbacks } = block.payload;
26
- const entry = ArtifactServices[artifact.type];
27
- if (!entry) return null;
28
- const Component = entry.Component;
29
- return /* @__PURE__ */ jsx(Component, { artifact, labels, callbacks });
30
- }
31
-
32
- // src/components/blocks/block-services/artifact-ref-block-service.ts
33
- var ArtifactRefBlockService = {
34
- type: blockTypes.ARTIFACT_REF,
35
- Component: ArtifactRefBlock,
36
- /**
37
- * Delegates to the registered artifact component's `toClipboardText`. Falls back to
38
- * `[Artifact: <title>]` via `artifactToClipboardText`'s own fallback when the artifact type
39
- * has no registered component, so copy never silently produces an empty string.
40
- */
41
- toClipboardText: (block) => artifactToClipboardText(block.payload.artifact)
42
- };
43
-
44
- // src/components/blocks/text-block/text-block.tsx
45
- import ReactMarkdown from "react-markdown";
46
- import remarkBreaks from "remark-breaks";
47
- import remarkGfm from "remark-gfm";
48
-
49
- // src/components/blocks/scrollable-table/scrollable-table-styles.ts
50
- import styled from "styled-components";
51
- var TableContainer = styled.div`
52
- position: relative;
53
- margin-block: 8px 16px;
54
- `;
55
- var TableScroll = styled.div`
56
- width: 100%;
57
- overflow-x: auto;
58
- overflow-y: hidden;
59
- `;
60
-
61
- // src/components/blocks/scrollable-table/scrollable-table.tsx
62
- import { jsx as jsx2 } from "react/jsx-runtime";
63
- function ScrollableTable({ node: _node, ...props }) {
64
- return /* @__PURE__ */ jsx2(TableContainer, { children: /* @__PURE__ */ jsx2(TableScroll, { children: /* @__PURE__ */ jsx2("table", { ...props }) }) });
65
- }
66
-
67
- // src/components/blocks/text-block/sanitize.ts
68
- function sanitize(text) {
69
- return text.split("\n").filter((line) => {
70
- const t = line.trim();
71
- return !(t.startsWith("<!-- table-title:") && t.endsWith("-->"));
72
- }).join("\n");
73
- }
74
-
75
- // src/components/blocks/text-block/text-block-styles.ts
76
- import styled2 from "styled-components";
77
- var TextBlockWrapper = styled2.div`
78
- ${typographyMixin(typographyTypes.GEIST_BODY_S_REGULAR)};
79
- color: ${colors["brown-100"]};
80
- word-break: break-word;
81
-
82
- & h1 {
83
- ${typographyMixin(typographyTypes.GEIST_HEADING_S_BOLD)};
84
- margin-block: 20px;
85
- }
86
-
87
- & h2 {
88
- ${typographyMixin(typographyTypes.GEIST_BODY_L_SEMI_BOLD)};
89
- margin-block: 12px;
90
- }
91
-
92
- & h3,
93
- & h4,
94
- & h5,
95
- & h6 {
96
- ${typographyMixin(typographyTypes.GEIST_BODY_M_SEMI_BOLD)};
97
- margin-block: 8px;
98
- }
99
-
100
- & strong {
101
- ${typographyMixin(typographyTypes.GEIST_BODY_S_SEMI_BOLD)};
102
- }
103
-
104
- & p {
105
- margin-block: 0px;
106
- }
107
-
108
- & p + p {
109
- margin-block: 8px 0px;
110
- }
111
-
112
- & ul,
113
- & ol {
114
- margin-block: 16px;
115
- padding-left: 20px;
116
- }
117
-
118
- & li + li {
119
- margin-block: 4px;
120
- }
121
-
122
- & ul {
123
- list-style-type: disc;
124
- }
125
-
126
- & ul ul {
127
- list-style-type: circle;
128
- }
129
-
130
- & ul ul ul {
131
- list-style-type: square;
132
- }
133
-
134
- & ol {
135
- list-style-type: decimal;
136
- }
137
-
138
- & ol ol {
139
- list-style-type: lower-alpha;
140
- }
141
-
142
- & ol ol ol {
143
- list-style-type: lower-roman;
144
- }
145
-
146
- & a {
147
- color: inherit;
148
- text-decoration-line: underline;
149
- text-decoration-style: dotted;
150
- text-decoration-thickness: auto;
151
- text-underline-offset: auto;
152
- text-underline-position: from-font;
153
- transition: color 75ms ease-in-out;
154
-
155
- &:hover {
156
- color: ${colors["blue-600"]};
157
- }
158
- }
159
-
160
- & hr {
161
- border: none;
162
- border-top: 1px solid ${colors["brown-40"]};
163
- margin-block: 20px;
164
- }
165
-
166
- & table {
167
- margin: 0;
168
- width: 100%;
169
- border-collapse: collapse;
170
- }
171
-
172
- & th,
173
- & td {
174
- border: none;
175
- border-bottom: 1px solid ${colors["brown-40"]};
176
- text-align: left;
177
- }
178
-
179
- & th {
180
- ${typographyMixin(typographyTypes.GEIST_LABEL_CAPTION_MEDIUM)};
181
- padding: 8px;
182
- }
183
-
184
- & td {
185
- ${typographyMixin(typographyTypes.GEIST_BODY_XS_MEDIUM)};
186
- padding: 16px 8px;
187
- min-width: 150px;
188
- width: 1%;
189
- }
190
-
191
- & td strong {
192
- font-weight: inherit;
193
- font-size: inherit;
194
- line-height: inherit;
195
- }
196
-
197
- & blockquote {
198
- margin-block: 4px;
199
- margin-inline: 0px;
200
- padding-left: 16px;
201
- border-left: 4px solid ${colors["brown-40"]};
202
- color: ${colors["brown-80"]};
203
- }
204
- `;
205
-
206
- // src/components/blocks/text-block/text-block.tsx
207
- import { jsx as jsx3 } from "react/jsx-runtime";
208
- var markdownComponents = {
209
- table: ((tableProps) => /* @__PURE__ */ jsx3(ScrollableTable, { ...tableProps }))
210
- };
211
- function TextBlock({ block }) {
212
- return /* @__PURE__ */ jsx3(TextBlockWrapper, { children: /* @__PURE__ */ jsx3(ReactMarkdown, { remarkPlugins: [remarkGfm, remarkBreaks], components: markdownComponents, children: sanitize(block.payload.text) }) });
213
- }
214
-
215
- // src/components/blocks/block-services/text-block-service.ts
216
- var TextBlockService = {
217
- type: blockTypes.TEXT,
218
- Component: TextBlock,
219
- /**
220
- * Converts the block's markdown source to plain text so the final clipboard value is clean
221
- * when pasted into tools that don't render markdown. Each block type owns its own clipboard
222
- * conversion so `useCopyToClipboard` can write text verbatim without a second pass.
223
- */
224
- toClipboardText: (block) => markdownToPlainText(block.payload.text)
225
- };
226
-
227
- // src/components/blocks/block-services/index.ts
228
- var BlockServices = {
229
- [blockTypes.TEXT]: TextBlockService,
230
- [blockTypes.ARTIFACT_REF]: ArtifactRefBlockService
231
- };
232
-
233
- // src/components/blocks/block/block.tsx
234
- import { jsx as jsx4 } from "react/jsx-runtime";
235
- function Block({ block }) {
236
- const service = BlockServices[block.type];
237
- if (!service) return null;
238
- const { Component } = service;
239
- return /* @__PURE__ */ jsx4(Component, { block });
240
- }
241
-
242
- export {
243
- blockTypes,
244
- ArtifactRefBlock,
245
- ArtifactRefBlockService,
246
- TextBlockService,
247
- BlockServices,
248
- Block
249
- };
250
- //# sourceMappingURL=chunk-46ZYTBX5.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/components/blocks/types.ts","../src/components/blocks/artifact-ref-block/artifact-ref-block.tsx","../src/components/blocks/block-services/artifact-ref-block-service.ts","../src/components/blocks/text-block/text-block.tsx","../src/components/blocks/scrollable-table/scrollable-table-styles.ts","../src/components/blocks/scrollable-table/scrollable-table.tsx","../src/components/blocks/text-block/sanitize.ts","../src/components/blocks/text-block/text-block-styles.ts","../src/components/blocks/block-services/text-block-service.ts","../src/components/blocks/block-services/index.ts","../src/components/blocks/block/block.tsx"],"sourcesContent":["import type { ComponentType } from \"react\";\n\nimport type { ArtifactCallbacksOf, ArtifactLabelsOf, ArtifactRecord } from \"@/components/artifacts/types\";\n\n/** Block-type discriminator constants. Mirrors the wire shape from io-server / shapes-agent. */\nexport const blockTypes = {\n TEXT: \"TEXT\",\n ARTIFACT_REF: \"ARTIFACT_REF\",\n} as const;\n\n/** Discriminator for content blocks. */\nexport type BlockType = (typeof blockTypes)[keyof typeof blockTypes];\n\n/** Plain-text content block — rendered as Markdown. */\nexport interface TextBlockRecord {\n /** Block-type discriminator. */\n type: typeof blockTypes.TEXT;\n /** Type-specific data; kept as `payload` to match the wire shape 1:1. */\n payload: {\n /** The Markdown source for this text segment. */\n text: string;\n };\n}\n\n/**\n * Reference-to-an-artifact content block — dispatched to the per-type artifact component.\n *\n * The artifact-domain types (`ArtifactRecord`, `TableArtifactRecord`, etc.) live in\n * `@/components/artifacts/types` so the artifact layer can be consumed independently of the\n * block layer (e.g. by a future surface that renders an artifact outside chat). The block layer\n * imports them here to wrap an artifact in an `ARTIFACT_REF` block.\n *\n * `ArtifactRefBlockRecord` is a *distributed* union over `ArtifactRecord` branches — TS narrows\n * `labels` and `callbacks` to the slice matching `artifact.type` via `ArtifactLabelsOf<T>` /\n * `ArtifactCallbacksOf<T>`. Without this distribution, `Partial<ChartLabels | TableLabels>`\n * collapses to `Partial<ChartLabels> | Partial<TableLabels>` and TS accepts chart-only keys on\n * a TABLE block's payload (the discriminator becomes load-bearing only at runtime, not in the\n * type system).\n */\nexport type ArtifactRefBlockRecord = ArtifactRecord extends infer TArtifact\n ? TArtifact extends ArtifactRecord\n ? {\n /** Block-type discriminator. */\n type: typeof blockTypes.ARTIFACT_REF;\n /** Type-specific data; kept as `payload` to match the wire shape 1:1. */\n payload: {\n /** The artifact's stable identifier, used for keying / future cross-message reuse. */\n artifactId: string;\n /** Inlined artifact metadata — clients fetch the underlying data via `artifact.protectedAsset.presignedUrl`. */\n artifact: TArtifact;\n /**\n * Optional consumer-supplied labels for the rendered artifact's action buttons + status\n * messages — a flat slice narrowed by `artifact.type`. NOT a wire field; the consumer\n * constructs this from their i18n layer when materialising the block-record from the\n * server payload, then passes the augmented block to `<Block>`. The dispatcher forwards\n * the slice directly to the matching renderer — no per-type key wrapping (the block\n * already carries the discriminator).\n *\n * TS catches cross-type assignments here: `block.payload.labels = { downloadCsv: 'X' }`\n * on a `TableArtifactRecord`-typed block is a type error because `downloadCsv` isn't a\n * `TableArtifactLabels` key.\n */\n labels?: Partial<ArtifactLabelsOf<TArtifact>>;\n /**\n * Optional side-effect callbacks for actions on the rendered artifact (e.g. `onDownload`).\n * Flat slice narrowed by `artifact.type` via `ArtifactCallbacksOf<T>`. NOT a wire field\n * — the consumer wires this once to bridge artifact actions to their analytics /\n * observability layer.\n */\n callbacks?: Partial<ArtifactCallbacksOf<TArtifact>>;\n };\n }\n : never\n : never;\n\n/** Discriminated union of all supported content blocks. Additive — new types extend this union. */\nexport type BlockRecord = TextBlockRecord | ArtifactRefBlockRecord;\n\n/** Props every concrete block component receives from the dispatcher. */\nexport interface BlockComponentProps<TBlock extends BlockRecord = BlockRecord> {\n /** The block to render. */\n block: TBlock;\n}\n\n/** Per-block-type service — registry entry that pairs a discriminator with its concrete component. */\nexport interface BlockService<TBlock extends BlockRecord = BlockRecord> {\n /** Discriminator value this service handles. */\n type: BlockType;\n /** React component that renders a block of this type. */\n Component: ComponentType<BlockComponentProps<TBlock>>;\n /**\n * Returns a plain-text or markdown string representation of this block suitable for clipboard\n * copy. Called by `copyMessageText` when the user clicks \"copy message\".\n *\n * - `TextBlockService`: returns `block.payload.text` verbatim (markdown preserved).\n * - `ArtifactRefBlockService`: delegates to `ArtifactServices[artifact.type].toClipboardText`.\n * Falls back to `[Artifact: <title>]` when the artifact type has no registered component.\n *\n * Optional so external `BlockService` implementors don't break on upgrade — when omitted, the\n * aggregator treats the block as contributing the empty string (silently skipped). Implementations\n * must be pure, synchronous functions — no async, no hooks, no side effects.\n */\n toClipboardText?: (block: TBlock) => string;\n}\n","import type { ComponentType, ReactNode } from \"react\";\n\nimport { ArtifactServices } from \"@/components/artifacts/artifact-services\";\nimport type { ArtifactRefBlockRecord, BlockComponentProps } from \"../types\";\n\n/**\n * Renders an `ARTIFACT_REF` block by dispatching the inlined artifact to its per-type component.\n *\n * The cast at the seam is necessary because the registry-based dispatch can't statically prove\n * type-T narrowing — but it's not hiding a bug. `ArtifactRefBlockRecord` is a distributed union\n * over `ArtifactRecord` branches (see `blocks/types.ts`), so TS catches wrong-shape labels at the\n * consumer's construction site: a chart-labels bag assigned to a TABLE block's payload is a type\n * error there, not silently forwarded to the wrong renderer here.\n *\n * Unknown artifact types render as `null` — the map only contains types we know how to render,\n * so an older clay client receiving a future artifact type degrades silently rather than\n * crashing the chat.\n */\nexport function ArtifactRefBlock({ block }: BlockComponentProps<ArtifactRefBlockRecord>): ReactNode {\n const { artifact, labels, callbacks } = block.payload;\n const entry = ArtifactServices[artifact.type];\n if (!entry) return null;\n // The registry stores `ArtifactService<ArtifactRecord>` (the union), so `entry.Component` is\n // `ComponentType<ArtifactServiceProps<ArtifactRecord>>`. The labels/callbacks props in the\n // narrower per-type `ArtifactServiceProps<TArtifact>` are `Partial<ArtifactLabelsOf<TArtifact>>`\n // which the union doesn't satisfy positionally. Cast forwards the values verbatim — the\n // runtime invariant is provably correct because the distributed `ArtifactRefBlockRecord` type\n // enforces it at construction.\n const Component = entry.Component as ComponentType<{\n artifact: typeof artifact;\n labels?: typeof labels;\n callbacks?: typeof callbacks;\n }>;\n return <Component artifact={artifact} labels={labels} callbacks={callbacks} />;\n}\n","import { artifactToClipboardText } from \"@/components/artifacts/artifact-services\";\nimport { ArtifactRefBlock } from \"../artifact-ref-block/artifact-ref-block\";\nimport { blockTypes } from \"../types\";\nimport type { ArtifactRefBlockRecord, BlockService } from \"../types\";\n\nexport const ArtifactRefBlockService: BlockService<ArtifactRefBlockRecord> = {\n type: blockTypes.ARTIFACT_REF,\n Component: ArtifactRefBlock,\n /**\n * Delegates to the registered artifact component's `toClipboardText`. Falls back to\n * `[Artifact: <title>]` via `artifactToClipboardText`'s own fallback when the artifact type\n * has no registered component, so copy never silently produces an empty string.\n */\n toClipboardText: (block) => artifactToClipboardText(block.payload.artifact),\n};\n","import type { ReactNode } from \"react\";\nimport ReactMarkdown from \"react-markdown\";\nimport remarkBreaks from \"remark-breaks\";\nimport remarkGfm from \"remark-gfm\";\n\nimport { ScrollableTable } from \"../scrollable-table/scrollable-table\";\nimport type { BlockComponentProps, TextBlockRecord } from \"../types\";\nimport { sanitize } from \"./sanitize\";\nimport { TextBlockWrapper } from \"./text-block-styles\";\n\nconst markdownComponents = {\n table: ((tableProps: { node?: unknown; children?: ReactNode }) => (\n <ScrollableTable {...tableProps} />\n )) as React.ComponentType<unknown>,\n};\n\n/** Renders a TEXT block as Markdown with GFM (tables, strikethrough) and line breaks. */\nexport function TextBlock({ block }: BlockComponentProps<TextBlockRecord>): ReactNode {\n return (\n <TextBlockWrapper>\n <ReactMarkdown remarkPlugins={[remarkGfm, remarkBreaks]} components={markdownComponents}>\n {sanitize(block.payload.text)}\n </ReactMarkdown>\n </TextBlockWrapper>\n );\n}\n","import styled from \"styled-components\";\n\nexport const TableContainer = styled.div`\n position: relative;\n margin-block: 8px 16px;\n`;\n\nexport const TableScroll = styled.div`\n width: 100%;\n overflow-x: auto;\n overflow-y: hidden;\n`;\n","import { type ReactNode } from \"react\";\n\nimport { TableContainer, TableScroll } from \"./scrollable-table-styles\";\n\n/** Props for the ScrollableTable component. */\ninterface ScrollableTableProps {\n /** Unused node prop from react-markdown. */\n node?: unknown;\n /** Table children elements. */\n children?: ReactNode;\n}\n\n/** Renders a markdown table inside a horizontally scrollable wrapper. Read-only — actions live on artifact-tables. */\nexport function ScrollableTable({ node: _node, ...props }: ScrollableTableProps): ReactNode {\n return (\n <TableContainer>\n <TableScroll>\n <table {...props} />\n </TableScroll>\n </TableContainer>\n );\n}\n","// Strips io-server's `<!-- table-title: ... -->` HTML-comment metadata before rendering.\n// The marker is emitted by the server's CSV-export pipeline so the export button can label\n// the file; it has no place in the visual output. If the server-side format changes\n// (prefix, spacing, closing-tag style), this filter silently stops matching and the\n// raw HTML comment leaks into rendered Markdown — keep this contract aligned with io-server.\nexport function sanitize(text: string): string {\n return text\n .split(\"\\n\")\n .filter((line) => {\n const t = line.trim();\n return !(t.startsWith(\"<!-- table-title:\") && t.endsWith(\"-->\"));\n })\n .join(\"\\n\");\n}\n","import styled from \"styled-components\";\n\nimport { colors } from \"@/tokens/colors\";\nimport { typographyMixin, typographyTypes } from \"@/tokens/typography\";\n\nexport const TextBlockWrapper = styled.div`\n ${typographyMixin(typographyTypes.GEIST_BODY_S_REGULAR)};\n color: ${colors[\"brown-100\"]};\n word-break: break-word;\n\n & h1 {\n ${typographyMixin(typographyTypes.GEIST_HEADING_S_BOLD)};\n margin-block: 20px;\n }\n\n & h2 {\n ${typographyMixin(typographyTypes.GEIST_BODY_L_SEMI_BOLD)};\n margin-block: 12px;\n }\n\n & h3,\n & h4,\n & h5,\n & h6 {\n ${typographyMixin(typographyTypes.GEIST_BODY_M_SEMI_BOLD)};\n margin-block: 8px;\n }\n\n & strong {\n ${typographyMixin(typographyTypes.GEIST_BODY_S_SEMI_BOLD)};\n }\n\n & p {\n margin-block: 0px;\n }\n\n & p + p {\n margin-block: 8px 0px;\n }\n\n & ul,\n & ol {\n margin-block: 16px;\n padding-left: 20px;\n }\n\n & li + li {\n margin-block: 4px;\n }\n\n & ul {\n list-style-type: disc;\n }\n\n & ul ul {\n list-style-type: circle;\n }\n\n & ul ul ul {\n list-style-type: square;\n }\n\n & ol {\n list-style-type: decimal;\n }\n\n & ol ol {\n list-style-type: lower-alpha;\n }\n\n & ol ol ol {\n list-style-type: lower-roman;\n }\n\n & a {\n color: inherit;\n text-decoration-line: underline;\n text-decoration-style: dotted;\n text-decoration-thickness: auto;\n text-underline-offset: auto;\n text-underline-position: from-font;\n transition: color 75ms ease-in-out;\n\n &:hover {\n color: ${colors[\"blue-600\"]};\n }\n }\n\n & hr {\n border: none;\n border-top: 1px solid ${colors[\"brown-40\"]};\n margin-block: 20px;\n }\n\n & table {\n margin: 0;\n width: 100%;\n border-collapse: collapse;\n }\n\n & th,\n & td {\n border: none;\n border-bottom: 1px solid ${colors[\"brown-40\"]};\n text-align: left;\n }\n\n & th {\n ${typographyMixin(typographyTypes.GEIST_LABEL_CAPTION_MEDIUM)};\n padding: 8px;\n }\n\n & td {\n ${typographyMixin(typographyTypes.GEIST_BODY_XS_MEDIUM)};\n padding: 16px 8px;\n min-width: 150px;\n width: 1%;\n }\n\n & td strong {\n font-weight: inherit;\n font-size: inherit;\n line-height: inherit;\n }\n\n & blockquote {\n margin-block: 4px;\n margin-inline: 0px;\n padding-left: 16px;\n border-left: 4px solid ${colors[\"brown-40\"]};\n color: ${colors[\"brown-80\"]};\n }\n`;\n","import { markdownToPlainText } from \"@/utils/markdown-to-plain-text\";\nimport { TextBlock } from \"../text-block/text-block\";\nimport { blockTypes } from \"../types\";\nimport type { BlockService, TextBlockRecord } from \"../types\";\n\nexport const TextBlockService: BlockService<TextBlockRecord> = {\n type: blockTypes.TEXT,\n Component: TextBlock,\n /**\n * Converts the block's markdown source to plain text so the final clipboard value is clean\n * when pasted into tools that don't render markdown. Each block type owns its own clipboard\n * conversion so `useCopyToClipboard` can write text verbatim without a second pass.\n */\n toClipboardText: (block) => markdownToPlainText(block.payload.text),\n};\n","import { blockTypes, type BlockService, type BlockType } from \"../types\";\nimport { ArtifactRefBlockService } from \"./artifact-ref-block-service\";\nimport { TextBlockService } from \"./text-block-service\";\n\n// `BlockService<TBlock>` is invariant on `TBlock` via React's `ComponentType` (props are\n// contravariant). The concrete services overlap structurally with the base `BlockService` type\n// enough for a direct `as BlockService` cast. The registry dispatches by runtime discriminator —\n// `BlockServices[block.type]` always resolves to the matching concrete service — so the cast is sound.\nexport const BlockServices: Record<BlockType, BlockService> = {\n [blockTypes.TEXT]: TextBlockService as BlockService,\n [blockTypes.ARTIFACT_REF]: ArtifactRefBlockService as BlockService,\n};\n","import type { ReactNode } from \"react\";\n\nimport { BlockServices } from \"../block-services\";\nimport type { BlockRecord } from \"../types\";\n\n/** Props for the Block dispatcher. */\ninterface BlockProps {\n /** The block to render. Dispatched to the registered concrete component for its `type`. */\n block: BlockRecord;\n}\n\n/**\n * Dispatches a single block to its registered concrete component. Unknown types render as a no-op.\n *\n * Generic by design: per-block-type extras (e.g. localized artifact labels for `ARTIFACT_REF`)\n * live on the block's `payload`, not as sibling props here. That keeps the dispatcher's contract\n * shallow — third-party `BlockService` implementors only need to handle `{ block }` — and lets\n * the consumer materialise a fully-decorated block once (from the server payload + their i18n\n * layer) instead of threading sibling props through every dispatch site.\n */\nexport function Block({ block }: BlockProps): ReactNode {\n const service = BlockServices[block.type];\n if (!service) return null;\n const { Component } = service;\n return <Component block={block} />;\n}\n"],"mappings":"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","names":["jsx","styled","styled","jsx","jsx"]}