@shapesos/clay 0.16.0 → 0.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) 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 +976 -69
  5. package/dist/artifacts.cjs.map +1 -1
  6. package/dist/artifacts.d.cts +20 -3
  7. package/dist/artifacts.d.ts +20 -3
  8. package/dist/artifacts.js +13 -5
  9. package/dist/blocks.cjs +690 -85
  10. package/dist/blocks.cjs.map +1 -1
  11. package/dist/blocks.css +1 -1
  12. package/dist/blocks.d.cts +3 -3
  13. package/dist/blocks.d.ts +3 -3
  14. package/dist/blocks.js +5 -5
  15. package/dist/button.cjs +3 -1
  16. package/dist/button.cjs.map +1 -1
  17. package/dist/button.js +2 -2
  18. package/dist/capture.cjs +3 -1
  19. package/dist/capture.cjs.map +1 -1
  20. package/dist/capture.js +2 -2
  21. package/dist/chart.cjs +16 -36
  22. package/dist/chart.cjs.map +1 -1
  23. package/dist/chart.d.cts +3 -11
  24. package/dist/chart.d.ts +3 -11
  25. package/dist/chart.js +2 -2
  26. package/dist/chat.cjs +714 -109
  27. package/dist/chat.cjs.map +1 -1
  28. package/dist/chat.d.cts +2 -2
  29. package/dist/chat.d.ts +2 -2
  30. package/dist/chat.js +6 -6
  31. package/dist/{chunk-UZKNSAAD.js → chunk-7MY4X4YL.js} +3 -3
  32. package/dist/chunk-BHHJOG5X.js +1 -0
  33. package/dist/{chunk-GAZRZZRF.js → chunk-CATIRDZP.js} +5 -5
  34. package/dist/{chunk-B4U37WIH.js → chunk-CFCLVJIO.js} +2 -2
  35. package/dist/{chunk-PYDVANWI.js → chunk-E36VI2HA.js} +2 -2
  36. package/dist/{chunk-U3IXCSV6.js → chunk-G4NXVFXU.js} +18 -40
  37. package/dist/chunk-G4NXVFXU.js.map +1 -0
  38. package/dist/{chunk-JAGNXEWK.js → chunk-GK4XW4BC.js} +2 -2
  39. package/dist/{chunk-4VE6ZXXW.js → chunk-MQP57DOP.js} +3 -3
  40. package/dist/{chunk-2YCBDSPK.js → chunk-QR4VBGK4.js} +673 -42
  41. package/dist/chunk-QR4VBGK4.js.map +1 -0
  42. package/dist/{chunk-JF3P66JF.js → chunk-SN3ORQSJ.js} +4 -2
  43. package/dist/{chunk-JF3P66JF.js.map → chunk-SN3ORQSJ.js.map} +1 -1
  44. package/dist/{chunk-PGR2MDDU.js → chunk-W66OJM55.js} +2 -2
  45. package/dist/{chunk-7OYIDM42.js → chunk-XKURJ72I.js} +2 -2
  46. package/dist/{dom-to-image-NATF3446.js → dom-to-image-QB7KMTAE.js} +3 -3
  47. package/dist/icon.cjs +3 -1
  48. package/dist/icon.cjs.map +1 -1
  49. package/dist/icon.js +2 -2
  50. package/dist/index.cjs +780 -167
  51. package/dist/index.cjs.map +1 -1
  52. package/dist/index.d.cts +3 -3
  53. package/dist/index.d.ts +3 -3
  54. package/dist/index.js +19 -11
  55. package/dist/keyboard-shortcut.cjs +3 -1
  56. package/dist/keyboard-shortcut.cjs.map +1 -1
  57. package/dist/keyboard-shortcut.js +2 -2
  58. package/dist/text-area.cjs +3 -1
  59. package/dist/text-area.cjs.map +1 -1
  60. package/dist/text-area.js +2 -2
  61. package/dist/tokens.cjs +3 -1
  62. package/dist/tokens.cjs.map +1 -1
  63. package/dist/tokens.d.cts +1 -0
  64. package/dist/tokens.d.ts +1 -0
  65. package/dist/tokens.js +1 -1
  66. package/dist/{types-CW45GBXl.d.cts → types-BDYPVP2N.d.cts} +1 -1
  67. package/dist/{types-D1b8v7EY.d.ts → types-BMwa4bPW.d.ts} +1 -1
  68. package/dist/{types-GCkXbyFm.d.ts → types-C8JtnwNY.d.ts} +200 -4
  69. package/dist/{types-BzXPBy6s.d.cts → types-DTdDFU3O.d.cts} +200 -4
  70. package/package.json +1 -1
  71. package/dist/chunk-2YCBDSPK.js.map +0 -1
  72. package/dist/chunk-RSRNSWJN.js +0 -1
  73. package/dist/chunk-U3IXCSV6.js.map +0 -1
  74. /package/dist/{chunk-UZKNSAAD.js.map → chunk-7MY4X4YL.js.map} +0 -0
  75. /package/dist/{chunk-RSRNSWJN.js.map → chunk-BHHJOG5X.js.map} +0 -0
  76. /package/dist/{chunk-GAZRZZRF.js.map → chunk-CATIRDZP.js.map} +0 -0
  77. /package/dist/{chunk-B4U37WIH.js.map → chunk-CFCLVJIO.js.map} +0 -0
  78. /package/dist/{chunk-PYDVANWI.js.map → chunk-E36VI2HA.js.map} +0 -0
  79. /package/dist/{chunk-JAGNXEWK.js.map → chunk-GK4XW4BC.js.map} +0 -0
  80. /package/dist/{chunk-4VE6ZXXW.js.map → chunk-MQP57DOP.js.map} +0 -0
  81. /package/dist/{chunk-PGR2MDDU.js.map → chunk-W66OJM55.js.map} +0 -0
  82. /package/dist/{chunk-7OYIDM42.js.map → chunk-XKURJ72I.js.map} +0 -0
  83. /package/dist/{dom-to-image-NATF3446.js.map → dom-to-image-QB7KMTAE.js.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/artifacts/artifact-base.ts","../src/components/artifacts/chart-artifact/types.ts","../src/components/artifacts/table-artifact/types.ts","../src/components/artifacts/deck-artifact/types.ts","../node_modules/@tabler/icons-react/src/defaultAttributes.ts","../node_modules/@tabler/icons-react/src/createReactComponent.ts","../node_modules/@tabler/icons-react/src/icons/IconAlertCircle.ts","../node_modules/@tabler/icons-react/src/icons/IconArrowsMaximize.ts","../node_modules/@tabler/icons-react/src/icons/IconArrowsMinimize.ts","../node_modules/@tabler/icons-react/src/icons/IconCheck.ts","../node_modules/@tabler/icons-react/src/icons/IconChevronLeft.ts","../node_modules/@tabler/icons-react/src/icons/IconChevronRight.ts","../node_modules/@tabler/icons-react/src/icons/IconCopy.ts","../node_modules/@tabler/icons-react/src/icons/IconDownload.ts","../node_modules/@tabler/icons-react/src/icons/IconFileSpreadsheet.ts","../node_modules/@tabler/icons-react/src/icons/IconLoader2.ts","../node_modules/@tabler/icons-react/src/icons/IconPhoto.ts","../node_modules/@tabler/icons-react/src/icons/IconThumbDown.ts","../node_modules/@tabler/icons-react/src/icons/IconThumbUp.ts","../src/components/artifacts/artifact-copy-as-image-button/artifact-copy-as-image-button.tsx","../src/components/ui/tooltip.tsx","../src/components/artifacts/artifact-copy-as-image-button/use-copy-as-image.ts","../src/components/artifacts/artifact-download-button/artifact-download-button.tsx","../src/components/artifacts/artifact-download-menu/artifact-download-menu.tsx","../src/components/ui/dropdown-menu.tsx","../src/components/artifacts/chart-artifact/chart-artifact.tsx","../src/components/ai-elements/artifact.tsx","../src/components/artifacts/status-message.tsx","../src/components/artifacts/chart-artifact/chart-variant-services/constants.ts","../src/components/artifacts/chart-artifact/chart-variant-services/bar-chart-variant-component.tsx","../src/components/artifacts/chart-artifact/chart-variant-services/shared-column-keys.ts","../src/components/artifacts/chart-artifact/chart-variant-services/shared-validators.ts","../src/components/artifacts/chart-artifact/chart-variant-services/bar-chart-variant-service.tsx","../src/components/artifacts/chart-artifact/chart-variant-services/line-chart-variant-component.tsx","../src/components/artifacts/chart-artifact/chart-variant-services/line-chart-variant-service.tsx","../src/components/artifacts/chart-artifact/chart-variant-services/pie-chart-variant-component.tsx","../src/components/artifacts/chart-artifact/chart-variant-services/pie-chart-variant-service.tsx","../src/components/artifacts/chart-artifact/chart-variant-services/index.ts","../src/components/artifacts/chart-artifact/chart-artifact-content.tsx","../src/components/artifacts/parse-csv.ts","../src/components/artifacts/use-rows-artifact-data.ts","../src/components/artifacts/chart-artifact/use-chart-artifact-data.ts","../src/components/artifacts/chart-artifact/validate.ts","../src/components/artifacts/deck-artifact/deck-artifact.tsx","../src/components/artifacts/deck-artifact/deck-artifact-content.tsx","../src/components/artifacts/table-artifact/table-artifact-content.constants.ts","../src/components/artifacts/table-artifact/table-artifact-content.utils.ts","../src/components/artifacts/table-artifact/table-artifact-content.tsx","../src/components/artifacts/deck-artifact/deck-markdown.tsx","../src/components/artifacts/deck-artifact/slide-renderers.tsx","../src/components/artifacts/table-artifact/validate.ts","../src/components/artifacts/deck-artifact/validate.ts","../src/components/artifacts/table-artifact/table-artifact.tsx","../src/components/artifacts/table-artifact/use-table-artifact-data.ts","../src/components/artifacts/chart-artifact/to-clipboard-text.ts","../src/components/artifacts/artifact-services/chart-artifact-service.ts","../src/components/artifacts/deck-artifact/to-clipboard-text.ts","../src/components/artifacts/artifact-services/deck-artifact-service.ts","../src/components/artifacts/table-artifact/to-clipboard-text.ts","../src/components/artifacts/artifact-services/table-artifact-service.ts","../src/components/artifacts/artifact-services/index.ts"],"sourcesContent":["/**\n * Shared artifact primitives — the discriminator constant and the cross-variant `ArtifactBase`\n * shape that every concrete artifact record extends.\n *\n * Pulled into its own file so the per-type `types.ts` files (`table-artifact/types.ts`,\n * `chart-artifact/types.ts`) and the union assembler (`artifacts/types.ts`) can all depend on\n * one canonical source without creating a circular module graph between them.\n */\n\n/**\n * Artifact-type discriminator constants. Uppercase to match the server-side `artifactTypes`\n * constant — io-server stores `artifact.type === \"TABLE\"` (aligned with the GraphQL enum).\n */\nexport const artifactTypes = {\n TABLE: \"TABLE\",\n CHART: \"CHART\",\n DECK: \"DECK\",\n} as const;\n\n/** Discriminator for artifact types. Additive — new types extend this union. */\nexport type ArtifactType = (typeof artifactTypes)[keyof typeof artifactTypes];\n\n/** Common shape every artifact record carries. Discriminated by `type`. */\nexport interface ArtifactBase<TType extends ArtifactType, TConfig> {\n /** Artifact-type discriminator. */\n type: TType;\n /** Human-readable label. Nullable when the artifact has no title. */\n title: string | null;\n /** Rendering config — shape varies per artifact type. */\n config: TConfig;\n /** Protected-asset metadata pointing to the underlying data payload. Null while the asset is unavailable. */\n protectedAsset: {\n /** Time-limited URL the renderer fetches the artifact's data from. */\n presignedUrl: string;\n } | null;\n}\n\n/**\n * A row inside an artifact's parsed CSV. Every cell is the raw string from the CSV (or `null`\n * for the empty string). Used by every artifact kind whose data wire is a CSV (`TABLE`, `CHART`).\n * The renderer never coerces — type-aware formatting is a server concern (the CSV's contents\n * are what the user sees).\n */\nexport type ArtifactDataRow = Record<string, string | null>;\n","import type { ChartSeries } from \"@/components/chart/types\";\nimport { artifactTypes, type ArtifactBase } from \"../artifact-base\";\nimport type { ChartConfig } from \"./chart-variant-services\";\n\nexport type { ChartConfig } from \"./chart-variant-services\";\nexport type { LineChartConfig, BarChartConfig, PieChartConfig } from \"./chart-variant-services\";\n\n/**\n * Optional stats embedded in `ChartConfig` so clients can pre-render UI before fetching the\n * actual CSV (e.g. a skeleton sized to the eventual row count). Mirrors `TableStats`.\n */\nexport interface ChartStats {\n /** Total number of rows in the materialised CSV. */\n rowCount: number;\n}\n\n/**\n * Cross-variant base for `ChartConfig`. Carries the fields every variant shares — series\n * descriptors and optional stats. Per-variant config interfaces (in `chart-variant-services/`)\n * extend this with their own required fields.\n *\n * Note: `series` is always present on the wire (matching the GraphQL `[ChartSeries!]!` shape)\n * even for PIE — PIE just receives an empty array. Keeping it on the base mirrors the wire shape\n * exactly and avoids TS object-literal freshness errors for callers that explicitly set\n * `series: []` on a pie config.\n */\nexport interface ChartConfigBase {\n /** Series descriptors in render order. LINE/BAR consume these; PIE ignores in favour of `categoryKey`+`valueKey`. */\n series: ChartSeries[];\n /** Optional metadata describing the underlying data — used for loading-state UI. */\n stats?: ChartStats;\n}\n\n/** Concrete chart artifact record. */\nexport type ChartArtifactRecord = ArtifactBase<typeof artifactTypes.CHART, ChartConfig>;\n\n/**\n * Localized labels for the `CHART` artifact's user-facing strings. Covers action buttons AND\n * status messages — see `TableArtifactLabels` for the same shape philosophy. Extends as the\n * chart artifact grows its action set (e.g. future `copyAsPng`); optional keys document\n * anticipated additions without forcing every consumer to provide them today.\n */\nexport interface ChartArtifactLabels {\n /** Tooltip + aria-label on the Download menu's trigger button (icon-only, opens the format\n * picker). The trigger is action-shaped (just \"Download\"); the menu items spell out the\n * format. */\n download: string;\n /** Label for the \"Download CSV\" item inside the Download menu. */\n downloadCsv: string;\n /** Label for the \"Download PNG\" item inside the Download menu. */\n downloadPng: string;\n /** Tooltip + aria-label on the \"Copy Image\" button in the chart artifact header (idle state).\n * Transient working / success / error tooltips remain in English — they flash for ~1.5s and the\n * cost of translating them is rarely worth it. */\n copyAsImage: string;\n /** Status message shown when no presigned URL is available or the chart config is malformed. */\n unavailable: string;\n /** Status message shown when the fetch / parse fails at runtime. */\n loadError: string;\n /** Status message shown while the CSV is being fetched. */\n loading: string;\n /** Status message shown when the fetched CSV has zero data rows. */\n empty: string;\n /**\n * Category labels treated as the \"Others\" bucket in pie charts (case-insensitive). Matching\n * slices are pinned to the end of the sweep and rendered in neutral grey. The agent writes a\n * translated bucket name in non-English UIs (e.g. \"אחרים\", \"Otros\") — consumer passes the\n * localized list here so clay can detect the bucket regardless of language. English fallback\n * is `[\"Others\", \"Other\"]`. Pass `[]` to disable Others detection (every slice from the palette).\n */\n othersCategoryLabels: readonly string[];\n}\n\n/** English fallback for `ChartArtifactLabels`. Used when the consumer hasn't passed `labels`. */\nexport const DEFAULT_CHART_ARTIFACT_LABELS: ChartArtifactLabels = {\n download: \"Download\",\n downloadCsv: \"Download CSV\",\n downloadPng: \"Download PNG\",\n copyAsImage: \"Copy Image\",\n unavailable: \"Chart data is unavailable.\",\n loadError: \"Couldn't load chart data.\",\n loading: \"Loading chart…\",\n empty: \"No data to chart.\",\n othersCategoryLabels: [\"Others\", \"Other\"],\n};\n\n/**\n * Consumer-supplied side-effect callbacks for actions on the chart artifact. Mirrors\n * `ChartArtifactLabels`'s shape philosophy: one entry per action this artifact exposes\n * (download today; future \"Copy as PNG\" etc.). Every callback is optional — consumers wire\n * only the actions they care about. The artifact passes its own `ChartArtifactRecord` as the\n * only argument so consumers can derive analytics context (title, config.variant, etc.) without\n * a per-block closure at every render site.\n *\n * The callback fires alongside the default browser behaviour (e.g. `onDownload` does not\n * prevent the anchor's download navigation) — it's purely for analytics / observability hooks.\n */\nexport interface ChartArtifactCallbacks {\n /** Fires when the \"Download CSV\" menu item is selected. Browser-native download still happens. */\n onDownload?: (artifact: ChartArtifactRecord) => void;\n /** Fires when the \"Download PNG\" menu item is selected. Runs alongside the rasterise — fires\n * regardless of whether the rasterise ultimately succeeds (failures surface via console.warn). */\n onDownloadPng?: (artifact: ChartArtifactRecord) => void;\n /** Fires when the Copy Image button is clicked. Fires regardless of whether the copy ultimately\n * succeeds (the result is reported via the button's own state flash) — use this only for\n * analytics / observability, not for retrying failed copies. */\n onCopyAsImage?: (artifact: ChartArtifactRecord) => void;\n}\n","import { artifactTypes, type ArtifactBase, type ArtifactDataRow } from \"../artifact-base\";\n\n/**\n * A single column descriptor for a `TableArtifact`. Carries the ORDER and LABEL for the\n * rendered table — `key` selects a column from the CSV header; `label` overrides the rendered\n * header text. The CSV is always the source of truth for the data shape; clay never coerces,\n * reformats, or interprets cell values.\n */\nexport interface TableColumn {\n /** Matches a header in the CSV. Required. Used to project + order the CSV's columns. */\n key: string;\n /** Human-readable header text. Required. */\n label: string;\n /**\n * Accepted in the wire payload but not consulted by the renderer — every cell is rendered as\n * the raw CSV string (or empty for null). Format conversion is a server concern; if a column\n * needs to be \"Yes/No\" instead of \"true/false\", the server should emit it that way in the CSV.\n * Kept in the interface so callers that pass `type` (e.g. io-client) don't get a type error.\n */\n type?: \"string\" | \"number\" | \"date\" | \"boolean\";\n /**\n * Accepted in the wire payload but not consulted by the renderer — every column is left-aligned\n * by design. Kept in the interface for forward-compat; restore per-column alignment by editing\n * `table-artifact-content.tsx` if a future use case justifies it.\n */\n align?: \"left\" | \"center\" | \"right\";\n}\n\n/**\n * Optional stats embedded in `TableConfig` so clients can pre-render UI before fetching the\n * actual data payload (e.g. N loading shimmers sized to the eventual row count).\n *\n * Backward-compat: optional everywhere — older artifacts persisted before this field shipped\n * still validate. The renderer falls back to a single status-message line when `stats` is absent.\n */\nexport interface TableStats {\n /** Total number of rows in the materialised CSV. Lets the loading branch render shimmers. */\n rowCount: number;\n}\n\n/** Rendering config for a table artifact, stored at `artifact.config` (JSONB on the server). */\nexport interface TableConfig {\n /** Column descriptors in render order. */\n columns: TableColumn[];\n /** Optional metadata describing the underlying data — used for loading-state UI. */\n stats?: TableStats;\n}\n\n/** Alias retained for callers typed against the table-specific name. */\nexport type TableRow = ArtifactDataRow;\n\n/** Concrete table artifact record. */\nexport type TableArtifactRecord = ArtifactBase<typeof artifactTypes.TABLE, TableConfig>;\n\n/**\n * Localized labels for the `TABLE` artifact's user-facing strings. Covers both action buttons\n * (download today; future \"Copy as CSV\", etc.) AND status messages (loading / error / empty /\n * unavailable). Consumer passes the bag from their i18n layer; clay falls back to English\n * defaults when omitted (per-key Partial merge).\n */\nexport interface TableArtifactLabels {\n /** Tooltip + aria-label on the Download CSV button in the table artifact header. */\n download: string;\n /**\n * Status message shown when no presigned URL is available (permanent server-side absence) or\n * the artifact config is malformed. Both states reduce to \"the underlying file isn't usable\".\n */\n unavailable: string;\n /** Status message shown when the fetch / parse fails at runtime (transient). */\n loadError: string;\n /** Status message shown while the CSV is being fetched and `stats.rowCount` is not known. */\n loading: string;\n /** Empty-state row inside the table body when the fetched CSV has zero data rows. */\n empty: string;\n}\n\n/** English fallback for `TableArtifactLabels`. Used when the consumer hasn't passed `labels`. */\nexport const DEFAULT_TABLE_ARTIFACT_LABELS: TableArtifactLabels = {\n download: \"Download CSV\",\n unavailable: \"Table data is unavailable.\",\n loadError: \"Couldn't load table data.\",\n loading: \"Loading table…\",\n empty: \"No rows to display.\",\n};\n\n/**\n * Consumer-supplied side-effect callbacks for actions on the table artifact. Mirrors\n * `ChartArtifactCallbacks` — see that interface for the shape philosophy. Every callback is\n * optional; the artifact passes its own `TableArtifactRecord` as the only argument so consumers\n * can derive analytics context without a per-block closure at every render site.\n */\nexport interface TableArtifactCallbacks {\n /** Fires when the Download CSV button is clicked. Browser-native download still happens. */\n onDownload?: (artifact: TableArtifactRecord) => void;\n}\n","import { artifactTypes, type ArtifactBase, type ArtifactDataRow } from \"../artifact-base\";\nimport { DEFAULT_CHART_ARTIFACT_LABELS, type ChartArtifactLabels, type ChartConfig } from \"../chart-artifact/types\";\nimport { DEFAULT_TABLE_ARTIFACT_LABELS, type TableArtifactLabels, type TableConfig } from \"../table-artifact/types\";\n\n/**\n * Block-type discriminator constants for `DeckBlock`. Uppercase to match the GraphQL\n * `DeckBlockType` enum (which serializes as `TEXT` / `CHART` / `TABLE`) and the existing CHART\n * `variant` precedent — the discriminator is identical end-to-end (agent → io-server → clay) with\n * no case mapping at any boundary. Use these instead of string literals so a typo is a\n * compile-time error rather than a silent wrong-branch at runtime.\n */\nexport const deckBlockTypes = {\n TEXT: \"TEXT\",\n CHART: \"CHART\",\n TABLE: \"TABLE\",\n} as const;\n\n/** Discriminator for the kinds of content a slide can carry. Additive — new kinds extend this. */\nexport type DeckBlockType = (typeof deckBlockTypes)[keyof typeof deckBlockTypes];\n\n/**\n * Slide-template kind discriminator constants for `DeckSlide`. Uppercase, end-to-end (agent →\n * io-server → clay), mirroring `deckBlockTypes`. Use these instead of string literals so a typo\n * is a compile-time error rather than a silent wrong-branch at runtime.\n */\nexport const deckSlideKinds = {\n TITLE: \"TITLE\",\n AGENDA: \"AGENDA\",\n SECTION: \"SECTION\",\n CONTENT: \"CONTENT\",\n CLOSING: \"CLOSING\",\n} as const;\n\n/** Discriminator for the visual template a slide renders with. */\nexport type DeckSlideKind = (typeof deckSlideKinds)[keyof typeof deckSlideKinds];\n\n/**\n * A markdown text block on a slide. Rendered with the same react-markdown pipeline as chat text\n * blocks (GFM + single-newline breaks), so `**bold**`, lists, and headings render as formatted\n * output rather than literal syntax.\n */\nexport interface DeckTextBlock {\n /** Discriminator. */\n type: typeof deckBlockTypes.TEXT;\n /** Markdown source for the block body. */\n markdown: string;\n}\n\n/**\n * A chart block on a slide. Carries its data inline (`rows`) alongside the same `ChartConfig` the\n * standalone `CHART` artifact uses — the deck renderer feeds both straight into\n * `ChartArtifactContent` with no fetch (decks have no presigned data payload).\n */\nexport interface DeckChartBlock {\n /** Discriminator. */\n type: typeof deckBlockTypes.CHART;\n /** Chart rendering config — reused verbatim from the `CHART` artifact. */\n config: ChartConfig;\n /** Inline data rows the chart plots. Each cell is a raw string (or `null`), matching the CSV wire. */\n rows: ArtifactDataRow[];\n}\n\n/**\n * A table block on a slide. Carries its data inline (`rows`) alongside the same `TableConfig` the\n * standalone `TABLE` artifact uses — the deck renderer feeds both into `TableArtifactContent`\n * (wrapping `rows` in a synthetic `\"ready\"` data state) with no fetch.\n */\nexport interface DeckTableBlock {\n /** Discriminator. */\n type: typeof deckBlockTypes.TABLE;\n /** Table rendering config — reused verbatim from the `TABLE` artifact. */\n config: TableConfig;\n /** Inline data rows. Each cell is a raw string (or `null`), matching the CSV wire. */\n rows: ArtifactDataRow[];\n}\n\n/**\n * Discriminated union of the content blocks a slide can render. Discriminated by `type`; data is\n * inline (no presigned-asset fetch). Older clay versions encountering a future block kind render\n * it as the unavailable-state rather than crashing — see `DeckArtifactContent`.\n */\nexport type DeckBlock = DeckTextBlock | DeckChartBlock | DeckTableBlock;\n\n/**\n * A single slide inside a `DeckArtifact`. Unlike `TABLE` / `CHART`, a deck carries NO underlying\n * CSV / protected-asset payload for rendering — its content lives entirely inline in\n * `config.slides[*].content`, so the renderer reads slides straight from `artifact.config` and\n * never fetches a presigned URL to draw them. (The presigned URL, when present, points at a\n * downloadable `.pptx` export — a separate path from rendering.)\n */\nexport interface DeckSlide {\n /** Slide heading, rendered prominently at the top of the slide. Required. */\n title: string;\n /** Ordered content blocks (text / chart / table) rendered top-to-bottom. May be empty. */\n content: DeckBlock[];\n /**\n * Visual template kind. Absent / unknown → treated as `\"CONTENT\"` (backward-compatible).\n * Use `deckSlideKinds` constants to avoid typos.\n */\n kind?: DeckSlideKind;\n /** Subtitle line shown below the main title. Used on TITLE slides; ignored by other kinds. */\n subtitle?: string;\n /** Short description paragraph. Used on TITLE slides below the subtitle. */\n description?: string;\n /**\n * Agenda items (AGENDA kind). Each string is one line item rendered with an ordinal\n * prefix (`01`, `02`, …) in Crimson Pro italic accent. Ignored by non-AGENDA kinds.\n */\n items?: string[];\n}\n\n/**\n * Optional stats embedded in `DeckConfig`. Mirrors `TableStats` / `ChartStats` — lets a consumer\n * render deck chrome (e.g. \"Slide 1 / N\") before deeply inspecting the slide array.\n *\n * Backward-compat: optional everywhere; the renderer derives the count from `slides.length` when\n * absent.\n */\nexport interface DeckStats {\n /** Total number of slides in the deck. */\n slideCount: number;\n}\n\n/**\n * Optional deck-level metadata embedded in `DeckConfig`. Injected by the client at render time;\n * clay renders each field if present and ignores it when absent.\n */\nexport interface DeckMeta {\n /** Short eyebrow / running header text shown at top-left of every slide (e.g. org name). */\n eyebrow?: string;\n /** Date string shown on the TITLE slide (e.g. \"Q2 2026\"). No formatting is applied — rendered as-is. */\n date?: string;\n /**\n * Theme identifier. Defaults to `\"shapes-light\"` when absent.\n * Currently only `\"shapes-light\"` is implemented; unknown values fall back to the default.\n */\n theme?: string;\n /**\n * Account logo URL injected by the client at render time. Clay renders `<img src={logoUrl}>`\n * (capped height, object-contain) at top-right of every slide when present. Omitted → no logo.\n *\n * CROSS-REPO DEVIATION: this field exists ONLY on clay's `DeckMeta`. io-server's `DeckMeta`\n * (and the GraphQL SDL) deliberately omit `logoUrl` — it is client-injected from the account's\n * branding and must NOT round-trip through the server. Keep the two `DeckMeta` shapes in sync on\n * every field EXCEPT this one. The renderer validates it against an http(s) allowlist before use.\n */\n logoUrl?: string;\n}\n\n/** Rendering config for a deck artifact, stored at `artifact.config` (JSONB on the server). */\nexport interface DeckConfig {\n /** Ordered slides. At least one; the renderer guards against an empty array. */\n slides: DeckSlide[];\n /** Optional metadata describing the deck — used for chrome before slide inspection. */\n stats?: DeckStats;\n /** Optional deck-level metadata (eyebrow, date, theme, logo). */\n meta?: DeckMeta;\n}\n\n/** Concrete deck artifact record. */\nexport type DeckArtifactRecord = ArtifactBase<typeof artifactTypes.DECK, DeckConfig>;\n\n/**\n * Localized labels for the `DECK` artifact's user-facing strings. Covers navigation controls,\n * the download action, and status messages. Consumer passes the bag from their i18n layer; clay\n * falls back to English defaults when omitted (per-key Partial merge).\n *\n * `chart` / `table` are nested bags forwarded to embedded chart / table blocks' renderers so\n * those inherit the consumer's translations (status / empty / loading strings). Both are optional;\n * the orchestrator defaults them to `DEFAULT_CHART_ARTIFACT_LABELS` / `DEFAULT_TABLE_ARTIFACT_LABELS`.\n */\nexport interface DeckArtifactLabels {\n /** aria-label on the \"previous slide\" navigation button. */\n previous: string;\n /** aria-label on the \"next slide\" navigation button. */\n next: string;\n /**\n * Slide-position indicator template. `{current}` and `{total}` are substituted at render time\n * (e.g. \"Slide {current} of {total}\" → \"Slide 2 of 5\").\n */\n position: string;\n /** Tooltip + aria-label on the Download (`.pptx`) button in the deck artifact header. */\n download: string;\n /** Status message shown when the deck config is malformed or has no slides. */\n unavailable: string;\n /** aria-label on the expand / enter-fullscreen button in the artifact header. */\n enterFullscreen: string;\n /** aria-label on the collapse / exit-fullscreen button in the fullscreen overlay. */\n exitFullscreen: string;\n /** \"Thank you\" closing message shown on the CLOSING slide. */\n thankYou: string;\n /** \"Questions?\" prompt shown on the CLOSING slide below the thank-you message. */\n questions: string;\n /** Fallback title for the AGENDA slide when the slide's `title` is empty. */\n agenda: string;\n /** Label bag forwarded to embedded chart blocks. Defaults to `DEFAULT_CHART_ARTIFACT_LABELS`. */\n chart?: ChartArtifactLabels;\n /** Label bag forwarded to embedded table blocks. Defaults to `DEFAULT_TABLE_ARTIFACT_LABELS`. */\n table?: TableArtifactLabels;\n}\n\n/** English fallback for `DeckArtifactLabels`. Used when the consumer hasn't passed `labels`. */\nexport const DEFAULT_DECK_ARTIFACT_LABELS: DeckArtifactLabels = {\n previous: \"Previous slide\",\n next: \"Next slide\",\n position: \"Slide {current} of {total}\",\n download: \"Download\",\n unavailable: \"Deck content is unavailable.\",\n enterFullscreen: \"Fullscreen\",\n exitFullscreen: \"Exit fullscreen\",\n thankYou: \"Thank you\",\n questions: \"Questions?\",\n agenda: \"Agenda\",\n chart: DEFAULT_CHART_ARTIFACT_LABELS,\n table: DEFAULT_TABLE_ARTIFACT_LABELS,\n};\n\n/**\n * Consumer-supplied side-effect callbacks for actions on the deck artifact. Mirrors\n * `TableArtifactCallbacks` — every callback is optional and receives the `DeckArtifactRecord` so\n * consumers can derive analytics context without a per-block closure at every render site.\n */\nexport interface DeckArtifactCallbacks {\n /** Fires when the user navigates to a different slide. `slideIndex` is zero-based. */\n onNavigate?: (artifact: DeckArtifactRecord, slideIndex: number) => void;\n /** Fires when the Download (`.pptx`) button is clicked. Browser-native download still happens. */\n onDownload?: (artifact: DeckArtifactRecord) => void;\n}\n","export default {\n outline: {\n xmlns: 'http://www.w3.org/2000/svg',\n width: 24,\n height: 24,\n viewBox: '0 0 24 24',\n fill: 'none',\n stroke: 'currentColor',\n strokeWidth: 2,\n strokeLinecap: 'round',\n strokeLinejoin: 'round',\n },\n filled: {\n xmlns: 'http://www.w3.org/2000/svg',\n width: 24,\n height: 24,\n viewBox: '0 0 24 24',\n fill: 'currentColor',\n stroke: 'none',\n },\n};\n","import { forwardRef, createElement } from 'react';\nimport defaultAttributes from './defaultAttributes';\nimport type { IconNode, IconProps, Icon } from './types';\n\nconst createReactComponent = (\n type: 'outline' | 'filled',\n iconName: string,\n iconNamePascal: string,\n iconNode: IconNode,\n) => {\n const Component = forwardRef<SVGSVGElement, IconProps>(\n (\n { color = 'currentColor', size = 24, stroke = 2, title, className, children, ...rest }: IconProps,\n ref,\n ) =>\n createElement(\n 'svg',\n {\n ref,\n ...defaultAttributes[type],\n width: size,\n height: size,\n className: [`tabler-icon`, `tabler-icon-${iconName}`, className].join(' '),\n ...(type === 'filled'\n ? {\n fill: color,\n }\n : {\n strokeWidth: stroke,\n stroke: color,\n }),\n ...rest,\n },\n [\n title && createElement('title', { key: 'svg-title' }, title),\n ...iconNode.map(([tag, attrs]) => createElement(tag, attrs)),\n ...(Array.isArray(children) ? children : [children]),\n ],\n ),\n );\n\n Component.displayName = `${iconNamePascal}`;\n\n return Component;\n};\n\nexport default createReactComponent;\n","import createReactComponent from '../createReactComponent';\nimport { IconNode } from '../types';\n\nexport const __iconNode: 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\"}]]\n\nconst IconAlertCircle = createReactComponent('outline', 'alert-circle', 'AlertCircle', __iconNode);\n\nexport default IconAlertCircle;","import createReactComponent from '../createReactComponent';\nimport { IconNode } from '../types';\n\nexport const __iconNode: IconNode = [[\"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\"}]]\n\nconst IconArrowsMaximize = createReactComponent('outline', 'arrows-maximize', 'ArrowsMaximize', __iconNode);\n\nexport default IconArrowsMaximize;","import createReactComponent from '../createReactComponent';\nimport { IconNode } from '../types';\n\nexport const __iconNode: IconNode = [[\"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\"}]]\n\nconst IconArrowsMinimize = createReactComponent('outline', 'arrows-minimize', 'ArrowsMinimize', __iconNode);\n\nexport default IconArrowsMinimize;","import createReactComponent from '../createReactComponent';\nimport { IconNode } from '../types';\n\nexport const __iconNode: IconNode = [[\"path\",{\"d\":\"M5 12l5 5l10 -10\",\"key\":\"svg-0\"}]]\n\nconst IconCheck = createReactComponent('outline', 'check', 'Check', __iconNode);\n\nexport default IconCheck;","import createReactComponent from '../createReactComponent';\nimport { IconNode } from '../types';\n\nexport const __iconNode: IconNode = [[\"path\",{\"d\":\"M15 6l-6 6l6 6\",\"key\":\"svg-0\"}]]\n\nconst IconChevronLeft = createReactComponent('outline', 'chevron-left', 'ChevronLeft', __iconNode);\n\nexport default IconChevronLeft;","import createReactComponent from '../createReactComponent';\nimport { IconNode } from '../types';\n\nexport const __iconNode: IconNode = [[\"path\",{\"d\":\"M9 6l6 6l-6 6\",\"key\":\"svg-0\"}]]\n\nconst IconChevronRight = createReactComponent('outline', 'chevron-right', 'ChevronRight', __iconNode);\n\nexport default IconChevronRight;","import createReactComponent from '../createReactComponent';\nimport { IconNode } from '../types';\n\nexport const __iconNode: IconNode = [[\"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\"}]]\n\nconst IconCopy = createReactComponent('outline', 'copy', 'Copy', __iconNode);\n\nexport default IconCopy;","import createReactComponent from '../createReactComponent';\nimport { IconNode } from '../types';\n\nexport const __iconNode: IconNode = [[\"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\"}]]\n\nconst IconDownload = createReactComponent('outline', 'download', 'Download', __iconNode);\n\nexport default IconDownload;","import createReactComponent from '../createReactComponent';\nimport { IconNode } from '../types';\n\nexport const __iconNode: IconNode = [[\"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\"}]]\n\nconst IconFileSpreadsheet = createReactComponent('outline', 'file-spreadsheet', 'FileSpreadsheet', __iconNode);\n\nexport default IconFileSpreadsheet;","import createReactComponent from '../createReactComponent';\nimport { IconNode } from '../types';\n\nexport const __iconNode: IconNode = [[\"path\",{\"d\":\"M12 3a9 9 0 1 0 9 9\",\"key\":\"svg-0\"}]]\n\nconst IconLoader2 = createReactComponent('outline', 'loader-2', 'Loader2', __iconNode);\n\nexport default IconLoader2;","import createReactComponent from '../createReactComponent';\nimport { IconNode } from '../types';\n\nexport const __iconNode: IconNode = [[\"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\"}]]\n\nconst IconPhoto = createReactComponent('outline', 'photo', 'Photo', __iconNode);\n\nexport default IconPhoto;","import createReactComponent from '../createReactComponent';\nimport { IconNode } from '../types';\n\nexport const __iconNode: IconNode = [[\"path\",{\"d\":\"M7 13v-8a1 1 0 0 0 -1 -1h-2a1 1 0 0 0 -1 1v7a1 1 0 0 0 1 1h3a4 4 0 0 1 4 4v1a2 2 0 0 0 4 0v-5h3a2 2 0 0 0 2 -2l-1 -5a2 3 0 0 0 -2 -2h-7a3 3 0 0 0 -3 3\",\"key\":\"svg-0\"}]]\n\nconst IconThumbDown = createReactComponent('outline', 'thumb-down', 'ThumbDown', __iconNode);\n\nexport default IconThumbDown;","import createReactComponent from '../createReactComponent';\nimport { IconNode } from '../types';\n\nexport const __iconNode: IconNode = [[\"path\",{\"d\":\"M7 11v8a1 1 0 0 1 -1 1h-2a1 1 0 0 1 -1 -1v-7a1 1 0 0 1 1 -1h3a4 4 0 0 0 4 -4v-1a2 2 0 0 1 4 0v5h3a2 2 0 0 1 2 2l-1 5a2 3 0 0 1 -2 2h-7a3 3 0 0 1 -3 -3\",\"key\":\"svg-0\"}]]\n\nconst IconThumbUp = createReactComponent('outline', 'thumb-up', 'ThumbUp', __iconNode);\n\nexport default IconThumbUp;","import { IconAlertCircle, IconCheck, IconCopy, IconLoader2, type Icon as TablerIcon } from \"@tabler/icons-react\";\nimport { useCallback, type ReactNode } from \"react\";\n\nimport { IconButton } from \"@/components/icon-button/icon-button\";\nimport { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from \"@/components/ui/tooltip\";\n\nimport type { ArtifactCopyAsImageButtonProps, CopyAsImageStatus } from \"./types\";\nimport { useCopyAsImage } from \"./use-copy-as-image\";\n\n/**\n * Trim error messages surfaced in the failure-state tooltip / aria-label. Long S3 presigned URLs\n * or stringified stack frames from `html-to-image`'s internals can otherwise produce multi-KB\n * strings that blow out tooltips and leak unnecessary detail.\n *\n * Inlined here (rather than re-exported from `@/capture/dom-to-image`) so this lightweight UI\n * component doesn't pull the entire capture module into chat/blocks/artifacts chunks via a\n * static import — the capture module's font data URLs (~100 KB) load lazily on first click via\n * `useCopyAsImage`'s dynamic import.\n */\nconst MAX_ERROR_MESSAGE_CHARS = 120;\nfunction truncateErrorMessage(message: string, max = MAX_ERROR_MESSAGE_CHARS): string {\n return message.length <= max ? message : `${message.slice(0, max - 1)}…`;\n}\n\n/** English fallback label for the idle state. Each per-type artifact orchestrator passes a\n * localized label in via its own `labels` bag; this default keeps the button usable for any\n * consumer rendering it standalone (e.g. in Storybook). Mirrors `ArtifactDownloadButton`. */\nconst DEFAULT_LABEL = \"Copy Image\";\n\nconst ICON_BY_STATUS: Record<CopyAsImageStatus, TablerIcon> = {\n // `IconCopy` reads as the clipboard-copy affordance (action-shaped), distinct from the\n // download menu's format-shaped icons. The button only ever puts bytes on the clipboard, so\n // the visual signal stays consistent across copy-aware artifact types.\n idle: IconCopy,\n working: IconLoader2,\n success: IconCheck,\n error: IconAlertCircle,\n};\n\nconst TRANSIENT_TOOLTIP_BY_STATUS: Partial<Record<CopyAsImageStatus, string>> = {\n working: \"Copying…\",\n success: \"Copied\",\n error: \"Copy failed\",\n};\n\n/**\n * Reusable \"copy artifact as image\" button for artifact headers. Drop it inside\n * `<ArtifactActions>` alongside `ArtifactDownloadButton`.\n *\n * Captures the DOM node referenced by `targetRef`, rasterises it to PNG, and writes the result\n * to the system clipboard. Must be invoked from a user gesture (the button click is enough).\n *\n * The button flashes a check icon on success and an alert icon on failure, reverting to its\n * idle photo icon after ~1.5s. While capturing, it disables itself and shows a spinner.\n *\n * On failure, the rejection is logged via `console.warn` so production errors are visible in\n * observability tooling (any consumer wiring a global console transport — Sentry, Datadog,\n * Logfire — picks them up). The user-visible affordance is the error-state flash + tooltip.\n */\nexport function ArtifactCopyAsImageButton({\n targetRef,\n label = DEFAULT_LABEL,\n disabled = false,\n onClick,\n}: ArtifactCopyAsImageButtonProps): ReactNode {\n const { status, error, copy } = useCopyAsImage(targetRef);\n\n const handleClick = useCallback(() => {\n onClick?.();\n copy().catch((err: unknown) => {\n // Surface to observability tooling. The state machine handles the user-visible flash; this\n // is the channel by which a global console transport (Sentry, Datadog, Logfire) picks the\n // failure up so on-call can correlate clipboard / canvas / permission rejections.\n console.warn(\"[clay] ArtifactCopyAsImageButton: copy failed\", err);\n });\n }, [copy, onClick]);\n\n // Single source of truth for both the tooltip and the aria-label so screen-reader users hear\n // the same context that the tooltip displays — including the truncated error message in the\n // error state. (Without this, the aria-label was \"Copy failed\" while the tooltip surfaced the\n // specific reason; that divergence was flagged in PR #60 review.)\n const statusLabel =\n status === \"idle\"\n ? label\n : status === \"error\" && error\n ? `Copy failed: ${truncateErrorMessage(error.message)}`\n : TRANSIENT_TOOLTIP_BY_STATUS[status] ?? label;\n\n return (\n <TooltipProvider>\n <Tooltip>\n <TooltipTrigger asChild>\n <IconButton\n icon={ICON_BY_STATUS[status]}\n size=\"small\"\n onClick={handleClick}\n disabled={disabled || status === \"working\"}\n aria-label={statusLabel}\n />\n </TooltipTrigger>\n <TooltipContent>{statusLabel}</TooltipContent>\n </Tooltip>\n </TooltipProvider>\n );\n}\n","import * as React from \"react\";\nimport * as TooltipPrimitive from \"@radix-ui/react-tooltip\";\n\nimport { cn } from \"@/lib/utils\";\n\nconst TooltipProvider = TooltipPrimitive.Provider;\n\nconst Tooltip = TooltipPrimitive.Root;\n\nconst TooltipTrigger = TooltipPrimitive.Trigger;\n\nconst TooltipContent = React.forwardRef<\n React.ElementRef<typeof TooltipPrimitive.Content>,\n React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>\n>(({ className, sideOffset = 4, ...props }, ref) => (\n <TooltipPrimitive.Content\n ref={ref}\n sideOffset={sideOffset}\n className={cn(\n \"z-50 overflow-hidden rounded-md border bg-popover px-3 py-1.5 text-sm text-popover-foreground shadow-md animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-[--radix-tooltip-content-transform-origin]\",\n className\n )}\n {...props}\n />\n));\nTooltipContent.displayName = TooltipPrimitive.Content.displayName;\n\nexport { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };\n","import { useCallback, useEffect, useRef, useState, type RefObject } from \"react\";\n\nimport type { UseCopyAsImageOptions, UseCopyAsImageResult, CopyAsImageStatus } from \"./types\";\n\nconst DEFAULT_RESET_AFTER_MS = 1500;\n\n/**\n * Internal hook powering `ArtifactCopyAsImageButton`. Not exported from any clay entry point.\n *\n * Wraps `copyNodeAsPng` with a small state machine so the button can flash a success or error\n * indicator before reverting to its idle icon. Bails out cleanly when the ref is not attached\n * (the call returns rejected; status becomes `error` with a descriptive message).\n *\n * **Generalising for a non-image copy button**: the state machine + auto-reset timer +\n * ref-not-attached guard are all generic to \"trigger an async action, flash success/error,\n * revert to idle\". To reuse this shape for any other copy affordance (copy table CSV, copy\n * link, copy code block), refactor in two moves rather than re-implementing it:\n *\n * 1. Rename to `useAsyncFlash`, replace the `await copyNodeAsPng(...)` line with an\n * injected `action: () => Promise<void>` parameter, drop the `targetRef` argument (the\n * caller's action closure owns whatever target it needs).\n * 2. Move `useAsyncFlash` to `src/hooks/` (a new top-level hooks entry, or somewhere it can\n * be imported by both this button and the new copy-table-csv button).\n *\n * The capture-specific bits living here today (`copyNodeAsPng` dynamic import, target-ref\n * unattached message) become a thin wrapper:\n *\n * const action = useCallback(async () => {\n * const node = targetRef.current; if (!node) throw new Error(\"…\");\n * const { copyNodeAsPng } = await import(\"@/capture/dom-to-image\");\n * await copyNodeAsPng(node, nodeOptions);\n * }, [targetRef, nodeOptions]);\n * return useAsyncFlash(action, { resetAfterMs });\n *\n * Postponed until a second consumer appears — premature abstraction with one call site.\n */\nexport function useCopyAsImage(\n targetRef: RefObject<HTMLElement | null>,\n options: UseCopyAsImageOptions = {}\n): UseCopyAsImageResult {\n const [status, setStatus] = useState<CopyAsImageStatus>(\"idle\");\n const [error, setError] = useState<Error | null>(null);\n const resetTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n useEffect(() => {\n return () => {\n if (resetTimerRef.current !== null) clearTimeout(resetTimerRef.current);\n };\n }, []);\n\n // `copy` closes over `options` directly — no `latestOptions` ref + no-deps `useEffect`. The\n // earlier \"latest-ref\" pattern existed to keep `copy` referentially stable across renders,\n // but the only consumer is the button's `onClick`, which doesn't depend on identity. Adding\n // `options` to the dep list means `copy` recreates per render — negligible perf cost for\n // this hook.\n const copy = useCallback(async () => {\n if (resetTimerRef.current !== null) {\n clearTimeout(resetTimerRef.current);\n resetTimerRef.current = null;\n }\n\n const { resetAfterMs = DEFAULT_RESET_AFTER_MS, ...nodeOptions } = options;\n\n const scheduleReset = () => {\n if (resetAfterMs <= 0) return;\n resetTimerRef.current = setTimeout(() => {\n resetTimerRef.current = null;\n setStatus(\"idle\");\n setError(null);\n }, resetAfterMs);\n };\n\n const node = targetRef.current;\n if (!node) {\n const err = new Error(\"ArtifactCopyAsImageButton: target ref is not attached to a DOM node.\");\n setError(err);\n setStatus(\"error\");\n scheduleReset();\n throw err;\n }\n setError(null);\n setStatus(\"working\");\n try {\n // Dynamic import: defers the capture chunk (Geist + Crimson Pro data URLs +\n // html-to-image) until the first copy click. The bundler emits this as a separate\n // chunk that only loads on demand — a consumer who imports `@shapesos/clay/chat`\n // but never triggers a copy never downloads the bytes.\n const { copyNodeAsPng } = await import(\"@/capture/dom-to-image\");\n await copyNodeAsPng(node, nodeOptions);\n setStatus(\"success\");\n scheduleReset();\n } catch (err) {\n const wrapped = err instanceof Error ? err : new Error(String(err));\n setError(wrapped);\n setStatus(\"error\");\n scheduleReset();\n throw wrapped;\n }\n }, [targetRef, options]);\n\n return { status, error, copy };\n}\n","import { IconDownload } from \"@tabler/icons-react\";\nimport type { ReactNode } from \"react\";\n\nimport { IconButton } from \"@/components/icon-button/icon-button\";\nimport { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from \"@/components/ui/tooltip\";\n\n/** English fallback label. Each per-type artifact passes a localized label in via the\n * `labels` prop; this default keeps the button usable for any consumer rendering it standalone. */\nconst DEFAULT_LABEL = \"Download CSV\";\n\n/** Props for `ArtifactDownloadButton`. */\nexport interface ArtifactDownloadButtonProps {\n /**\n * URL to download the artifact's underlying bytes. The button is an `<a href download>` so the\n * browser uses whatever filename the server supplies via `Content-Disposition`. No client-side\n * filename invention.\n *\n * Renders nothing when the URL is not a safe `http(s):` scheme — guards against `javascript:` or\n * `data:` URIs smuggled into `presignedUrl`.\n */\n href: string;\n /**\n * Tooltip text + `aria-label` for the button. Optional — defaults to `\"Download CSV\"` so direct\n * consumers don't have to pass anything for the common case. Per-type artifact orchestrators\n * (`<ChartArtifact>`, `<TableArtifact>`) pass a translated value via their own `labels` prop so\n * the button reads in the user's language.\n */\n label?: string;\n /**\n * Optional callback fired on click — runs alongside the browser's native download navigation\n * (no `preventDefault`). Used for analytics / observability hooks; per-type artifact\n * orchestrators wire this from their `callbacks` prop.\n */\n onClick?: () => void;\n}\n\n/**\n * Reusable download button for artifact headers. Anchor-based so the browser handles the download\n * natively (respects `Content-Disposition`, falls back to URL basename). `rel=\"noopener noreferrer\"`\n * defends against `window.opener` leakage if the browser opens the URL instead of downloading.\n *\n * Drop this inside `<ArtifactActions>` in any per-type artifact orchestrator.\n */\nexport function ArtifactDownloadButton({\n href,\n label = DEFAULT_LABEL,\n onClick,\n}: ArtifactDownloadButtonProps): ReactNode {\n if (!isSafeDownloadUrl(href)) return null;\n return (\n <TooltipProvider>\n <Tooltip>\n <TooltipTrigger asChild>\n <IconButton\n as=\"a\"\n href={href}\n download\n rel=\"noopener noreferrer\"\n onClick={onClick}\n icon={IconDownload}\n size=\"small\"\n aria-label={label}\n />\n </TooltipTrigger>\n <TooltipContent>{label}</TooltipContent>\n </Tooltip>\n </TooltipProvider>\n );\n}\n\n/**\n * Returns `true` only for `http:` and `https:` URLs. Rejects `javascript:`, `data:`, and any\n * other scheme that could execute code on click.\n */\nexport function isSafeDownloadUrl(url: string): boolean {\n try {\n const parsed = new URL(url);\n return parsed.protocol === \"https:\" || parsed.protocol === \"http:\";\n } catch {\n return false;\n }\n}\n","import { IconDownload, IconFileSpreadsheet, IconPhoto } from \"@tabler/icons-react\";\nimport { useCallback, type ReactNode } from \"react\";\n\n// `downloadNodeAsPng` is lazy-loaded inside `handlePngSelect` below — see use-copy-as-image\n// for the bundle-size rationale. The capture chunk (Geist + Crimson Pro data URLs +\n// html-to-image) only loads when the user actually clicks \"Download PNG\".\nimport { isSafeDownloadUrl } from \"@/components/artifacts/artifact-download-button/artifact-download-button\";\nimport { IconButton } from \"@/components/icon-button/icon-button\";\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n} from \"@/components/ui/dropdown-menu\";\nimport { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from \"@/components/ui/tooltip\";\n\nimport type { ArtifactDownloadMenuProps } from \"./types\";\n\n/** English fallback labels for the trigger button + each menu item. Per-type artifact\n * orchestrators pass localised values via their own `labels` bag. */\nconst DEFAULT_TRIGGER_LABEL = \"Download\";\nconst DEFAULT_CSV_LABEL = \"Download CSV\";\nconst DEFAULT_PNG_LABEL = \"Download PNG\";\nconst DEFAULT_PNG_BASENAME = \"artifact\";\n\n/**\n * Reusable \"Download\" affordance for artifact headers. Renders as a single icon-button trigger;\n * clicking it opens a menu with one item per format the caller passes via `actions`. Today's\n * formats: CSV (presigned URL) + PNG (DOM-to-image rasterise). Future formats (SVG, XLSX, …)\n * slot into the same `actions` bag without touching the call-site prop list.\n *\n * Drop this inside `<ArtifactActions>` in any per-type artifact orchestrator that supports\n * multiple download formats. For single-format artifacts (e.g. a future table artifact that\n * only ships CSV), prefer the simpler `ArtifactDownloadButton` instead.\n *\n * The menu uses Radix's `DropdownMenu` primitive (vendored in `src/components/ui/dropdown-menu.tsx`)\n * with `asChild` on the trigger so clay's `IconButton` is the real button — preserves the\n * design-system look across all action affordances.\n */\nexport function ArtifactDownloadMenu({\n actions,\n triggerLabel = DEFAULT_TRIGGER_LABEL,\n}: ArtifactDownloadMenuProps): ReactNode {\n const { csv, png } = actions;\n // CSV item is hidden when the URL fails the safety check — keeps the safe-URL guard at one\n // layer instead of relying on every caller to filter unsafe URLs upstream. PNG has no\n // equivalent URL surface; it's always rendered when `actions.png` is present (the\n // `disabled` knob is the caller's gate for \"data not ready\").\n const showCsv = csv !== undefined && isSafeDownloadUrl(csv.href);\n const showPng = png !== undefined;\n\n const handlePngSelect = useCallback(async () => {\n if (!png) return;\n png.onSelect?.();\n const node = png.targetRef.current;\n if (!node) return;\n const filename = `${png.filename ?? DEFAULT_PNG_BASENAME}.png`;\n try {\n // Dynamic import: defers the capture chunk (Geist + Crimson Pro data URLs +\n // html-to-image) until the first PNG-download click. See `use-copy-as-image` for the\n // bundle-size rationale.\n const { downloadNodeAsPng } = await import(\"@/capture/dom-to-image\");\n await downloadNodeAsPng(node, filename);\n } catch (err) {\n // Surface to observability tooling. No state flash on the menu item itself — the dropdown\n // closes on select; a failed rasterise produces a console.warn for on-call to correlate.\n console.warn(\"[clay] ArtifactDownloadMenu: PNG download failed\", err);\n }\n }, [png]);\n\n return (\n <DropdownMenu>\n <TooltipProvider>\n <Tooltip>\n <TooltipTrigger asChild>\n <DropdownMenuTrigger asChild>\n <IconButton icon={IconDownload} size=\"small\" aria-label={triggerLabel} />\n </DropdownMenuTrigger>\n </TooltipTrigger>\n <TooltipContent>{triggerLabel}</TooltipContent>\n </Tooltip>\n </TooltipProvider>\n <DropdownMenuContent align=\"end\">\n {showCsv ? (\n // `asChild` lets Radix render the item as our anchor — the browser handles the\n // download natively and respects `Content-Disposition`. The analytics callback rides\n // on the anchor's own `onClick` (rather than Radix's `onSelect`) so the same handler\n // fires whether the user mouse-clicks the item, hits Enter on it, or activates it via\n // touch — `onSelect` is reliable only for keyboard activation when `asChild` is in use.\n <DropdownMenuItem asChild>\n <a href={csv.href} download rel=\"noopener noreferrer\" onClick={csv.onSelect}>\n <IconFileSpreadsheet />\n {csv.label ?? DEFAULT_CSV_LABEL}\n </a>\n </DropdownMenuItem>\n ) : null}\n {showPng ? (\n <DropdownMenuItem onSelect={handlePngSelect} disabled={png.disabled ?? false}>\n <IconPhoto />\n {png.label ?? DEFAULT_PNG_LABEL}\n </DropdownMenuItem>\n ) : null}\n </DropdownMenuContent>\n </DropdownMenu>\n );\n}\n","import * as React from \"react\";\nimport * as DropdownMenuPrimitive from \"@radix-ui/react-dropdown-menu\";\n\nimport { cn } from \"@/lib/utils\";\n\n/* ------------------------------------------------------------------------------------------------\n * Vendored shadcn `DropdownMenu` primitive (slim subset: Root / Trigger / Content / Item / Separator\n * / Label). Built on `@radix-ui/react-dropdown-menu`. Mirrors the same shape as the existing\n * `tooltip.tsx` vendor — only the styled leaves (`Content`, `Item`, etc.) carry Tailwind classes\n * that `forwardRef` apply; the plumbing primitives (`Root`, `Trigger`) re-export Radix directly so\n * future shadcn re-scaffolds drop in cleanly.\n *\n * Internal-only by convention (CLAUDE.md): not exported from any clay entry point. The chart\n * artifact is the only consumer today (`ArtifactDownloadMenu`). Promote to a public\n * `@shapesos/clay/dropdown-menu` entry only when something outside `src/components/artifacts/`\n * needs it.\n *\n * Triggers should use `asChild` so clay's `IconButton` slots in as the actual trigger element —\n * the project rule is \"no new vendored shadcn primitives without a need, and route inner buttons\n * through clay's `Button` / `IconButton`\".\n * ------------------------------------------------------------------------------------------------ */\n\nconst DropdownMenu = DropdownMenuPrimitive.Root;\n\nconst DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;\n\nconst DropdownMenuPortal = DropdownMenuPrimitive.Portal;\n\nconst DropdownMenuContent = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.Content>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content>\n>(({ className, sideOffset = 4, ...props }, ref) => (\n <DropdownMenuPrimitive.Portal>\n <DropdownMenuPrimitive.Content\n ref={ref}\n sideOffset={sideOffset}\n className={cn(\n // Stacking is consumer-configurable via the `--z-popover` CSS variable (default `99999`,\n // declared in clay's generated `_theme-tokens.css` :root block). Consumers whose chat or\n // modal layer sits even higher override on their own root once:\n // :root { --z-popover: 9999999; }\n // and every clay floating surface picks the new value up via cascade. The fallback in\n // `var(...,99999)` only kicks in if the consumer also strips the clay stylesheet — which\n // they shouldn't, but the safety net costs nothing.\n \"z-[var(--z-popover,99999)] min-w-[10rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md\",\n \"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95\",\n \"data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2\",\n className\n )}\n {...props}\n />\n </DropdownMenuPrimitive.Portal>\n));\nDropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName;\n\nconst DropdownMenuItem = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.Item>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & { inset?: boolean }\n>(({ className, inset, ...props }, ref) => (\n <DropdownMenuPrimitive.Item\n ref={ref}\n className={cn(\n \"relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors\",\n \"focus:bg-accent focus:text-accent-foreground\",\n \"data-[disabled]:pointer-events-none data-[disabled]:opacity-50\",\n \"[&_svg]:size-4 [&_svg]:shrink-0\",\n inset && \"pl-8\",\n className\n )}\n {...props}\n />\n));\nDropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName;\n\nconst DropdownMenuSeparator = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.Separator>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator>\n>(({ className, ...props }, ref) => (\n <DropdownMenuPrimitive.Separator ref={ref} className={cn(\"-mx-1 my-1 h-px bg-muted\", className)} {...props} />\n));\nDropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName;\n\nconst DropdownMenuLabel = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.Label>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & { inset?: boolean }\n>(({ className, inset, ...props }, ref) => (\n <DropdownMenuPrimitive.Label\n ref={ref}\n className={cn(\"px-2 py-1.5 text-sm font-semibold\", inset && \"pl-8\", className)}\n {...props}\n />\n));\nDropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName;\n\nexport {\n DropdownMenu,\n DropdownMenuTrigger,\n DropdownMenuPortal,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuSeparator,\n DropdownMenuLabel,\n};\n","import { useEffect, useMemo, useRef, type ReactNode } from \"react\";\n\nimport {\n Artifact,\n ArtifactActions,\n ArtifactContent,\n ArtifactHeader,\n ArtifactTitle,\n} from \"@/components/ai-elements/artifact\";\nimport { CAPTURE_EXCLUDE_ATTRIBUTE } from \"@/capture/capture-exclude-attribute\";\nimport { ArtifactCopyAsImageButton } from \"@/components/artifacts/artifact-copy-as-image-button/artifact-copy-as-image-button\";\nimport { isSafeDownloadUrl } from \"@/components/artifacts/artifact-download-button/artifact-download-button\";\nimport { ArtifactDownloadMenu } from \"@/components/artifacts/artifact-download-menu/artifact-download-menu\";\nimport {\n DEFAULT_CHART_ARTIFACT_LABELS,\n type ArtifactServiceProps,\n type ChartArtifactLabels,\n type ChartArtifactRecord,\n} from \"@/components/artifacts/types\";\nimport { StatusMessage } from \"../status-message\";\nimport { ChartArtifactContent } from \"./chart-artifact-content\";\nimport { useChartArtifactData } from \"./use-chart-artifact-data\";\nimport { isValidChartConfig } from \"./validate\";\n\nconst TITLE_CLASSES = \"text-sm font-semibold text-foreground\";\n\n/**\n * Chart height. Picks a single sensible size for all three variants so chat layout stays\n * predictable — mirrors `TableArtifact`'s `TABLE_MAX_BODY_HEIGHT` approach. Width is fluid via\n * recharts' `ResponsiveContainer`.\n */\nconst CHART_HEIGHT = 320;\n\n/**\n * Orchestrator for the `CHART`-typed artifact.\n *\n * Composes the ai-elements chrome primitives (`Artifact`, `ArtifactHeader`, `ArtifactTitle`,\n * `ArtifactActions`, `ArtifactContent`) directly around the chart body (`ChartArtifactContent`),\n * matching `TableArtifact`'s shape. Loading / error / unavailable / empty states render an inline\n * `StatusMessage`; the ready state delegates to per-variant recharts trees inside\n * `ChartArtifactContent`.\n *\n * The renderer is variant-agnostic at this level — it doesn't know about LINE vs BAR vs PIE.\n * Variant dispatch lives inside `ChartArtifactContent` so the orchestrator can focus on the\n * data lifecycle and the chrome.\n *\n * Labels for action buttons (download, copy-as-image) are pulled from the optional `labels`\n * prop with per-key fallback to `DEFAULT_CHART_ARTIFACT_LABELS`. Consumers translate via their\n * own i18n layer and pass the bag; non-i18n consumers omit it and get English defaults.\n *\n * `ArtifactCopyAsImageButton` snapshots the card root to PNG. The ref attaches directly to the\n * `<Artifact>` primitive (which forwards refs as of this PR), so the captured DOM contains the\n * artifact chrome exactly — no extra wrapper div, no layout-contract drift. The action toolbar\n * carries `data-clay-capture-exclude` so its own buttons don't end up in the captured image.\n */\nexport function ChartArtifact({ artifact, labels, callbacks }: ArtifactServiceProps<ChartArtifactRecord>): ReactNode {\n const validConfig = isValidChartConfig(artifact.config);\n const cardRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n if (validConfig) return;\n console.warn(\"[clay] ChartArtifact: malformed `config` — skipping render.\");\n }, [validConfig]);\n\n const presignedUrl = artifact.protectedAsset?.presignedUrl ?? null;\n\n // Merge consumer-supplied labels over the English defaults. Per-key Partial merge means a\n // consumer can translate `download` and leave future keys to fall back. Memoised so resolved\n // labels are referentially stable across renders.\n const resolvedLabels = useMemo<ChartArtifactLabels>(\n () => ({ ...DEFAULT_CHART_ARTIFACT_LABELS, ...(labels ?? {}) }),\n [labels]\n );\n\n // Hook is always called (Rules of Hooks). Pass null URL when config is invalid so the hook\n // short-circuits at its `if (!presignedUrl) return UNAVAILABLE` path — no network fetch fires\n // for an artifact whose config shape can't be rendered.\n const data = useChartArtifactData(\n validConfig ? presignedUrl : null,\n validConfig ? artifact.config : { variant: \"LINE\", series: [], xKey: \"x\" }\n );\n\n // Bridge the consumer's callbacks into the action buttons. Wrapped to capture `artifact` so\n // each callback receives analytics context. `undefined` (rather than a no-op) when no callback\n // is wired — keeps each item free of a redundant select listener.\n const { onDownload, onDownloadPng, onCopyAsImage } = callbacks ?? {};\n const handleDownloadCsvSelect = onDownload ? () => onDownload(artifact) : undefined;\n const handleDownloadPngSelect = onDownloadPng ? () => onDownloadPng(artifact) : undefined;\n const handleCopyAsImageClick = onCopyAsImage ? () => onCopyAsImage(artifact) : undefined;\n\n // The Copy-as-image action is meaningful only once the chart has actually rendered. Before\n // that, the captured PNG would either be a \"Loading…\" status message or nothing at all —\n // disable the button until the data resolves to a renderable state. Hidden entirely when the\n // chart config is malformed (the artifact would never render a chart, so a permanently-disabled\n // affordance is just noise).\n // Render gates:\n // - Copy button: visible only when the config is valid (a permanently-disabled affordance on\n // a malformed artifact is just noise). Disabled until the chart actually renders so the\n // captured PNG isn't a \"Loading…\" status message.\n // - Download menu: visible when there's *any* format to offer. PNG requires `validConfig`\n // (no chart → nothing to rasterise). CSV requires a *safe* presigned URL — `isSafeDownloadUrl`\n // gates against `javascript:` / `data:` URIs smuggled into the field, matching the guard\n // `ArtifactDownloadButton` applies on the item itself. Without this hoist, an unsafe URL\n // would surface the menu trigger but yield no usable items (Bugbot 1544f88a).\n const canCopyAsImage = data.status === \"ready\" && data.rows.length > 0;\n const canDownloadPng = data.status === \"ready\" && data.rows.length > 0;\n const isCsvDownloadAvailable = presignedUrl !== null && isSafeDownloadUrl(presignedUrl);\n const showDownloadMenu = (validConfig && canDownloadPng) || isCsvDownloadAvailable;\n const showActions = validConfig || showDownloadMenu;\n\n // Derive a friendly PNG filename from the artifact title so the saved file is recognisable.\n // Fallback to \"chart\" when no title is set OR when stripping non-alphanumerics produces an\n // empty string (e.g. title is \"???\" or all-emoji). The menu appends `.png` so we pass the\n // basename.\n //\n // `split → filter → join` (vs. a `.replace(/^-+|-+$/g, \"\")` trim) avoids a polynomial-regex\n // pattern CodeQL flags on user-controlled input. Functionally identical: any run of\n // non-alphanumerics becomes a single `-`, and leading/trailing empty splits are dropped.\n const pngFilename =\n (artifact.title ?? \"\")\n .toLowerCase()\n .split(/[^a-z0-9]+/)\n .filter(Boolean)\n .join(\"-\") || \"chart\";\n\n return (\n <Artifact ref={cardRef} className=\"shadow-none w-full rounded-2xl bg-background/70\">\n <ArtifactHeader className=\"bg-background/70\">\n {artifact.title ? <ArtifactTitle className={TITLE_CLASSES}>{artifact.title}</ArtifactTitle> : <span />}\n {showActions ? (\n <ArtifactActions {...{ [CAPTURE_EXCLUDE_ATTRIBUTE]: \"\" }}>\n {validConfig ? (\n <ArtifactCopyAsImageButton\n targetRef={cardRef}\n label={resolvedLabels.copyAsImage}\n disabled={!canCopyAsImage}\n onClick={handleCopyAsImageClick}\n />\n ) : null}\n {showDownloadMenu ? (\n <ArtifactDownloadMenu\n triggerLabel={resolvedLabels.download}\n actions={{\n csv: isCsvDownloadAvailable\n ? {\n href: presignedUrl ?? \"\",\n label: resolvedLabels.downloadCsv,\n onSelect: handleDownloadCsvSelect,\n }\n : undefined,\n png: validConfig\n ? {\n targetRef: cardRef,\n filename: pngFilename,\n label: resolvedLabels.downloadPng,\n disabled: !canDownloadPng,\n onSelect: handleDownloadPngSelect,\n }\n : undefined,\n }}\n />\n ) : null}\n </ArtifactActions>\n ) : null}\n </ArtifactHeader>\n {/*\n `flex-none` overrides ai-elements' default `flex-1`. In a column flex container\n `flex: 1 1 0%` sets `flex-basis: 0%` which beats the inline `height` per CSS flex spec —\n leaving the chart's `ResponsiveContainer` 0px tall with no visible SVG.\n `flex items-center justify-center` vertically + horizontally centres status messages\n (loading / error / unavailable / empty) in the 320px area; the chart itself fills the\n whole box because its `<ChartContainer>` sets `width:100% height:100%`.\n */}\n <ArtifactContent\n className=\"p-4 overflow-visible flex flex-none items-center justify-center\"\n style={{ height: CHART_HEIGHT }}\n >\n {renderBody(validConfig, artifact, data, resolvedLabels)}\n </ArtifactContent>\n </Artifact>\n );\n}\n\n/** Splits the body branches out so the orchestrator's JSX stays readable. Labels are pre-merged\n * by the caller, so each branch reads a single field on a complete object. */\nfunction renderBody(\n validConfig: boolean,\n artifact: ChartArtifactRecord,\n data: ReturnType<typeof useChartArtifactData>,\n labels: ChartArtifactLabels\n): ReactNode {\n if (!validConfig) {\n return <StatusMessage $tone=\"info\">{labels.unavailable}</StatusMessage>;\n }\n if (data.status === \"error\") {\n // No retry hook today — mirrors `TableArtifact`. Wire `onRetry` into `ArtifactService` when\n // a fresh-presigned-URL mint flow exists.\n return <StatusMessage $tone=\"error\">{labels.loadError}</StatusMessage>;\n }\n if (data.status === \"unavailable\") {\n return <StatusMessage $tone=\"info\">{labels.unavailable}</StatusMessage>;\n }\n if (data.status === \"loading\") {\n return <StatusMessage $tone=\"info\">{labels.loading}</StatusMessage>;\n }\n // data.status === \"ready\"\n if (data.rows.length === 0) {\n return <StatusMessage $tone=\"info\">{labels.empty}</StatusMessage>;\n }\n return <ChartArtifactContent config={artifact.config} rows={data.rows} labels={labels} />;\n}\n","import { forwardRef, type HTMLAttributes } from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\n/* ------------------------------------------------------------------------------------------------\n * Originally vendored from `@vercel/ai-elements`'s `artifact` registry entry — see\n * https://elements.ai-sdk.dev/api/registry/artifact.json. Slimmed to the chrome primitives clay\n * actually composes (`Artifact`, `ArtifactHeader`, `ArtifactTitle`, `ArtifactActions`,\n * `ArtifactContent`). The original entry also shipped `ArtifactClose`, `ArtifactDescription`, and\n * `ArtifactAction` — all dependent on the shadcn `Button` primitive, none of which clay consumes.\n * Action buttons inside the artifact header use clay's `IconButton` (see `TableArtifact`'s\n * `ExportCsvButton`) instead of a generic shadcn Button, so the design system stays single-sourced.\n *\n * Local clay-only change: `Artifact` is `forwardRef` so per-type orchestrators (e.g.\n * `ChartArtifact`) can ref the card root for DOM-to-image capture without wrapping it in an\n * extra `<div>` that would otherwise break the layout contract (parents query offsetWidth /\n * apply gap against the wrapper, not the artifact). Matches the `forwardRef` convention used\n * across `src/components/ui/*` and clay's own primitives.\n *\n * If a future ai-elements re-scaffold needs one of the dropped primitives back, re-add only the\n * piece consumers need and route its inner button through clay's `Button` / `IconButton` rather\n * than re-introducing the shadcn Button file.\n * ------------------------------------------------------------------------------------------------ */\n\nexport type ArtifactProps = HTMLAttributes<HTMLDivElement>;\n\nexport const Artifact = forwardRef<HTMLDivElement, ArtifactProps>(({ className, ...props }, ref) => (\n <div\n ref={ref}\n className={cn(\"flex flex-col overflow-hidden rounded-lg border bg-background shadow-sm\", className)}\n {...props}\n />\n));\nArtifact.displayName = \"Artifact\";\n\nexport type ArtifactHeaderProps = HTMLAttributes<HTMLDivElement>;\n\nexport const ArtifactHeader = ({ className, ...props }: ArtifactHeaderProps) => (\n <div className={cn(\"flex items-center justify-between border-b bg-muted/50 px-4 py-3\", className)} {...props} />\n);\n\nexport type ArtifactTitleProps = HTMLAttributes<HTMLParagraphElement>;\n\nexport const ArtifactTitle = ({ className, ...props }: ArtifactTitleProps) => (\n <p className={cn(\"font-medium text-foreground text-sm\", className)} {...props} />\n);\n\nexport type ArtifactActionsProps = HTMLAttributes<HTMLDivElement>;\n\nexport const ArtifactActions = ({ className, ...props }: ArtifactActionsProps) => (\n <div className={cn(\"flex items-center gap-1\", className)} {...props} />\n);\n\nexport type ArtifactContentProps = HTMLAttributes<HTMLDivElement>;\n\nexport const ArtifactContent = ({ className, ...props }: ArtifactContentProps) => (\n <div className={cn(\"flex-1 overflow-auto p-4\", className)} {...props} />\n);\n","import type { ReactNode } from \"react\";\n\nimport { createElement } from \"react\";\n\ninterface StatusMessageProps {\n /** Visual tone: \"error\" renders red copy, \"info\" renders muted brown copy. */\n $tone?: \"info\" | \"error\";\n children: ReactNode;\n}\n\n/**\n * Centred status copy rendered inside an artifact when there is nothing to show — loading\n * without a known size, error, or unavailable. Shared across every artifact kind that needs\n * an inline status surface; Tailwind-styled so it stays consistent with the artifact chrome.\n */\nexport function StatusMessage({ $tone, children }: StatusMessageProps): ReactNode {\n return createElement(\n \"div\",\n {\n className: `py-6 px-2 text-center text-sm font-medium leading-5 ${\n $tone === \"error\" ? \"text-red-600\" : \"text-brown-70\"\n }`,\n },\n children\n );\n}\n","/**\n * Shared constants used by every chart variant's render Component.\n *\n * Pulled into one module so a future tweak (e.g. changing the variants to render at a fixed\n * pixel height) is a one-line edit here instead of N matching edits across variant files.\n */\n\n/**\n * Height passed to every variant's chart-library component. `\"100%\"` flexes the chart inside\n * the artifact's content area — the orchestrator (`ChartArtifact`) owns the actual pixel\n * budget (via its own `CHART_HEIGHT`); the variant Components just fill the parent.\n */\nexport const VARIANT_CHART_HEIGHT = \"100%\" as const;\n","import { type ReactNode } from \"react\";\n\nimport { BarChart } from \"@/components/chart/bar-chart/bar-chart\";\nimport type { BarChartConfig } from \"./bar-chart-variant-service\";\nimport { VARIANT_CHART_HEIGHT } from \"./constants\";\nimport type { ChartVariantComponentProps } from \"./types\";\n\n/**\n * Render Component for the `BAR` chart variant. Maps the artifact's wire-shaped\n * `BarChartConfig` onto props for the high-level `<BarChart>` from `@shapesos/clay/chart`.\n *\n * Split out from `bar-chart-variant-service.ts` so the service file stays a pure declarative\n * registration (discriminator + validator + column keys + Component reference) — the React\n * tree lives here.\n */\nexport function BarChartVariantComponent({ config, rows }: ChartVariantComponentProps<BarChartConfig>): ReactNode {\n return (\n <BarChart\n data={rows}\n xKey={config.xKey}\n series={config.series}\n stacked={config.stacked}\n height={VARIANT_CHART_HEIGHT}\n />\n );\n}\n","import type { ChartSeries } from \"@/components/chart/types\";\nimport type { BarChartConfig } from \"./bar-chart-variant-service\";\nimport type { LineChartConfig } from \"./line-chart-variant-service\";\nimport type { PieChartConfig } from \"./pie-chart-variant-service\";\n\n/**\n * CSV-column-projection helpers — one per chart variant. Pulled into this shared module so each\n * variant service file stays focused on the service definition (variant discriminator,\n * isValidConfig, Component) and the column-key extraction lives next to its siblings.\n *\n * Line + Bar share the same shape (`xKey + series.key`) and use the same helper. Pie has its\n * own shape (`categoryKey + valueKey`).\n *\n * Used by each variant service's `columnKeys` field to drive `useChartArtifactData`'s CSV\n * projection (matches the table-artifact projection contract — keep only the columns the chart\n * actually reads, drop the rest).\n */\n\n/** Column keys for LINE / BAR variants: the x-axis column plus every series's data column. */\nexport function seriesBasedColumnKeys(config: LineChartConfig | BarChartConfig): string[] {\n return [config.xKey, ...config.series.map((s: ChartSeries) => s.key)];\n}\n\n/** Column keys for the PIE variant: the slice-label column plus the slice-value column. */\nexport function pieColumnKeys(config: PieChartConfig): string[] {\n return [config.categoryKey, config.valueKey];\n}\n","import type { ChartSeries } from \"@/components/chart/types\";\n\n/**\n * Minimum-viable series shape for LINE / BAR variants: non-empty string `key` + string `label`.\n * Pulled into a shared file so every variant service that consumes `series` reuses one definition.\n */\nexport function isValidChartSeries(series: unknown): series is ChartSeries {\n if (!series || typeof series !== \"object\") return false;\n const candidate = series as Record<string, unknown>;\n if (typeof candidate.key !== \"string\" || candidate.key.length === 0) return false;\n if (typeof candidate.label !== \"string\") return false;\n return true;\n}\n","import type { ChartConfigBase } from \"../types\";\nimport { BarChartVariantComponent } from \"./bar-chart-variant-component\";\nimport { seriesBasedColumnKeys } from \"./shared-column-keys\";\nimport { isValidChartSeries } from \"./shared-validators\";\nimport type { ChartVariantService } from \"./types\";\n\n/**\n * Rendering config for a `BAR` chart. `xKey` selects the categorical axis; each series\n * contributes one `<Bar>`. `stacked` collapses all series onto a single stack-id (so they sum\n * vertically); when false / omitted bars are grouped side-by-side per x-tick.\n */\nexport interface BarChartConfig extends ChartConfigBase {\n variant: \"BAR\";\n /** CSV column key used as the x-axis. */\n xKey: string;\n /** When true, stack all series on one stack-id. When false / omitted, group bars side-by-side. */\n stacked?: boolean;\n}\n\nfunction isValidBarChartConfig(config: unknown): config is BarChartConfig {\n if (!config || typeof config !== \"object\") return false;\n const candidate = config as Record<string, unknown>;\n if (candidate.variant !== \"BAR\") return false;\n if (typeof candidate.xKey !== \"string\" || candidate.xKey.length === 0) return false;\n if (!Array.isArray(candidate.series) || candidate.series.length === 0) return false;\n if (!candidate.series.every(isValidChartSeries)) return false;\n return true;\n}\n\nexport const BarChartVariantService: ChartVariantService<\"BAR\", BarChartConfig> = {\n variant: \"BAR\",\n isValidConfig: isValidBarChartConfig,\n columnKeys: seriesBasedColumnKeys,\n Component: BarChartVariantComponent,\n};\n","import { type ReactNode } from \"react\";\n\nimport { LineChart } from \"@/components/chart/line-chart/line-chart\";\nimport { VARIANT_CHART_HEIGHT } from \"./constants\";\nimport type { LineChartConfig } from \"./line-chart-variant-service\";\nimport type { ChartVariantComponentProps } from \"./types\";\n\n/**\n * Render Component for the `LINE` chart variant. Maps the artifact's wire-shaped\n * `LineChartConfig` onto props for the high-level `<LineChart>` from `@shapesos/clay/chart`.\n *\n * Split out from `line-chart-variant-service.ts` so the service file stays a pure declarative\n * registration — see `bar-chart-variant-component.tsx` for the rationale.\n */\nexport function LineChartVariantComponent({ config, rows }: ChartVariantComponentProps<LineChartConfig>): ReactNode {\n return <LineChart data={rows} xKey={config.xKey} series={config.series} height={VARIANT_CHART_HEIGHT} />;\n}\n","import type { ChartConfigBase } from \"../types\";\nimport { LineChartVariantComponent } from \"./line-chart-variant-component\";\nimport { seriesBasedColumnKeys } from \"./shared-column-keys\";\nimport { isValidChartSeries } from \"./shared-validators\";\nimport type { ChartVariantService } from \"./types\";\n\n/**\n * Rendering config for a `LINE` chart. `xKey` selects the categorical/temporal axis from the\n * CSV; each series contributes one `<Line>` overlaid against that axis.\n */\nexport interface LineChartConfig extends ChartConfigBase {\n variant: \"LINE\";\n /** CSV column key used as the x-axis. */\n xKey: string;\n}\n\nfunction isValidLineChartConfig(config: unknown): config is LineChartConfig {\n if (!config || typeof config !== \"object\") return false;\n const candidate = config as Record<string, unknown>;\n if (candidate.variant !== \"LINE\") return false;\n if (typeof candidate.xKey !== \"string\" || candidate.xKey.length === 0) return false;\n if (!Array.isArray(candidate.series) || candidate.series.length === 0) return false;\n if (!candidate.series.every(isValidChartSeries)) return false;\n return true;\n}\n\nexport const LineChartVariantService: ChartVariantService<\"LINE\", LineChartConfig> = {\n variant: \"LINE\",\n isValidConfig: isValidLineChartConfig,\n columnKeys: seriesBasedColumnKeys,\n Component: LineChartVariantComponent,\n};\n","import { type ReactNode } from \"react\";\n\nimport { PieChart } from \"@/components/chart/pie-chart/pie-chart\";\nimport { VARIANT_CHART_HEIGHT } from \"./constants\";\nimport type { PieChartConfig } from \"./pie-chart-variant-service\";\nimport type { ChartVariantComponentProps } from \"./types\";\n\n/**\n * Render Component for the `PIE` chart variant. Maps the artifact's wire-shaped\n * `PieChartConfig` onto props for the high-level `<PieChart>` from `@shapesos/clay/chart`,\n * threading the localized `othersCategoryLabels` from the resolved labels bag so non-English\n * UIs can auto-pin their translated \"Others\" bucket to the end of the sweep.\n *\n * Split out from `pie-chart-variant-service.ts` so the service file stays a pure declarative\n * registration — see `bar-chart-variant-component.tsx` for the rationale.\n */\nexport function PieChartVariantComponent({\n config,\n rows,\n labels,\n}: ChartVariantComponentProps<PieChartConfig>): ReactNode {\n return (\n <PieChart\n data={rows}\n categoryKey={config.categoryKey}\n valueKey={config.valueKey}\n othersCategoryLabels={labels.othersCategoryLabels}\n height={VARIANT_CHART_HEIGHT}\n />\n );\n}\n","import type { ChartConfigBase } from \"../types\";\nimport { PieChartVariantComponent } from \"./pie-chart-variant-component\";\nimport { pieColumnKeys } from \"./shared-column-keys\";\nimport type { ChartVariantService } from \"./types\";\n\n/**\n * Rendering config for a `PIE` chart. `categoryKey` names the slice; `valueKey` carries the\n * slice's numeric magnitude. One row of CSV becomes one slice. The base's `series` array is\n * always empty for pie — colors are resolved by row index from the palette.\n */\nexport interface PieChartConfig extends ChartConfigBase {\n variant: \"PIE\";\n /** CSV column key used as the slice label. */\n categoryKey: string;\n /** CSV column key used as the slice numeric value. */\n valueKey: string;\n}\n\nfunction isValidPieChartConfig(config: unknown): config is PieChartConfig {\n if (!config || typeof config !== \"object\") return false;\n const candidate = config as Record<string, unknown>;\n if (candidate.variant !== \"PIE\") return false;\n if (typeof candidate.categoryKey !== \"string\" || candidate.categoryKey.length === 0) return false;\n if (typeof candidate.valueKey !== \"string\" || candidate.valueKey.length === 0) return false;\n if (!Array.isArray(candidate.series)) return false;\n return true;\n}\n\nexport const PieChartVariantService: ChartVariantService<\"PIE\", PieChartConfig> = {\n variant: \"PIE\",\n isValidConfig: isValidPieChartConfig,\n columnKeys: pieColumnKeys,\n Component: PieChartVariantComponent,\n};\n","import type { ChartVariant } from \"@/components/chart/types\";\nimport { BarChartVariantService, type BarChartConfig } from \"./bar-chart-variant-service\";\nimport { LineChartVariantService, type LineChartConfig } from \"./line-chart-variant-service\";\nimport { PieChartVariantService, type PieChartConfig } from \"./pie-chart-variant-service\";\nimport type { ChartVariantService } from \"./types\";\n\n/**\n * Discriminated union of all supported chart variant configs. Assembled here (not in the parent\n * `types.ts`) because the union depends on each variant service file as its single source of\n * truth for that variant's config type.\n *\n * Extension pattern: add a new variant by creating one `*-chart-variant-service.ts` file and\n * extending this union + the `ChartVariantServices` map below. No edits to the dispatcher.\n */\nexport type ChartConfig = LineChartConfig | BarChartConfig | PieChartConfig;\n\nexport { type LineChartConfig, type BarChartConfig, type PieChartConfig };\nexport { LineChartVariantService, BarChartVariantService, PieChartVariantService };\nexport type { ChartVariantComponentProps, ChartVariantService } from \"./types\";\n\n/**\n * Map of chart-variant services keyed by `config.variant`. Mirrors the artifact / block service\n * registries. The chart-artifact dispatcher looks up `ChartVariantServices[config.variant]` at\n * runtime to resolve validation, column projection, and the render Component for the variant.\n *\n * `ChartVariantService<TVariant, TConfig>` is invariant in `TConfig` (props on the Component are\n * contravariant). Each concrete service overlaps structurally with the base `ChartVariantService`\n * enough for a direct `as ChartVariantService` cast at registration time — the dispatch only ever\n * pulls the matching variant out by runtime discriminator, so this is sound.\n */\nexport const ChartVariantServices: Record<ChartVariant, ChartVariantService> = {\n LINE: LineChartVariantService as ChartVariantService,\n BAR: BarChartVariantService as ChartVariantService,\n PIE: PieChartVariantService as ChartVariantService,\n};\n","import { type ReactNode } from \"react\";\n\nimport type { ArtifactDataRow } from \"@/components/artifacts/types\";\nimport { StatusMessage } from \"../status-message\";\nimport { ChartVariantServices } from \"./chart-variant-services\";\nimport type { ChartConfig, ChartArtifactLabels } from \"./types\";\n\n/**\n * Props for the chart-artifact variant dispatcher. Internal to the artifact path — the\n * orchestrator (`chart-artifact.tsx`) is the only caller. By the time these props are\n * constructed, `config` has been narrowed by `isValidChartConfig` and `rows` has resolved to\n * the `\"ready\"` state of the data hook.\n */\nexport interface ChartArtifactContentProps {\n /** Validated chart config — narrowed by `variant` for per-variant component selection. */\n config: ChartConfig;\n /** Parsed CSV rows. Caller guards `rows.length > 0` before rendering this. */\n rows: ArtifactDataRow[];\n /** Fully-resolved labels bag — supplies the fallback message when the variant is unsupported. */\n labels: ChartArtifactLabels;\n}\n\n/**\n * Variant dispatcher for `ChartArtifact`. Looks up the matching per-variant service in the\n * `ChartVariantServices` registry and renders its Component. Each variant service owns its own\n * config type, validation, column projection, and renderer — see `chart-variant-services/`.\n *\n * Unknown variants fall back to a status message (rather than throwing) so an older clay client\n * encountering a future variant degrades gracefully instead of crashing the chat.\n */\nexport function ChartArtifactContent({ config, rows, labels }: ChartArtifactContentProps): ReactNode {\n const service = ChartVariantServices[config.variant];\n if (!service) {\n return <StatusMessage $tone=\"info\">{labels.unavailable}</StatusMessage>;\n }\n // Registry guarantees the runtime variant matches the service's TConfig; loose at the type\n // boundary here is sound — see `ChartVariantServices` cast doc.\n const Component = service.Component as React.ComponentType<{\n config: ChartConfig;\n rows: ArtifactDataRow[];\n labels: ChartArtifactLabels;\n }>;\n return <Component config={config} rows={rows} labels={labels} />;\n}\n","import Papa from \"papaparse\";\n\nimport type { ArtifactDataRow } from \"./types\";\nimport type { ArtifactColumnLike } from \"./use-rows-artifact-data\";\n\n/**\n * Parses a CSV text payload fetched from a presigned URL into `ArtifactDataRow[]`.\n *\n * Shared across artifact kinds whose data wire is a CSV (`TABLE`, `CHART`). Uses `Papa.parse`\n * with `header: true` (first row is the header) and `skipEmptyLines: true`. Every cell arrives\n * as a string — the renderer NEVER coerces values to JS numbers / booleans / dates. Format\n * conversion is a server concern: if a column needs to be `\"Yes/No\"` or `\"$1,234\"`, the server\n * emits it that way in the CSV.\n *\n * Column projection rules:\n * - CSV header columns NOT in `columns[].key` are silently dropped.\n * - `columns[].key` values missing from the CSV header are silently omitted from row output —\n * client-side reconciliation warnings would let the client second-guess the server's contract.\n * Trust the CSV.\n *\n * Empty cells (`\"\"`) are returned as `null` so the renderer can distinguish \"missing value\"\n * from \"empty string\" downstream.\n *\n * Generic over the column shape so callers carrying richer column metadata (`TableColumn` with\n * `label` and the deprecated `type`/`align`) get back their own type rather than `unknown`.\n *\n * @param text Raw CSV text (may start with a UTF-8 BOM which Papa strips automatically).\n * @param columns Column descriptors — only `key` is consulted for projection.\n * @returns Rows in source order, projected to the configured columns.\n */\nexport function parseCsv<TColumn extends ArtifactColumnLike>(text: string, columns: TColumn[]): ArtifactDataRow[] {\n const result = Papa.parse<Record<string, string>>(text, {\n header: true,\n skipEmptyLines: true,\n dynamicTyping: false,\n });\n\n const csvHeaders = new Set(result.meta?.fields ?? []);\n const presentColumns = columns.filter((col) => csvHeaders.has(col.key));\n\n // No overlap between requested columns and CSV headers — return [] (treat as \"no data\")\n // rather than `result.data.length` rows of `{}`. The latter has positive length so the chart's\n // empty-state check (`data.rows.length === 0`) would miss it, and the chart would render\n // blank (no plottable fields) instead of the empty status message.\n if (presentColumns.length === 0) return [];\n\n return result.data.map((rawRow) => {\n const row: ArtifactDataRow = {};\n for (const column of presentColumns) {\n const value = rawRow[column.key];\n row[column.key] = value === undefined || value === \"\" ? null : value;\n }\n return row;\n });\n}\n","import { useEffect, useMemo, useState } from \"react\";\n\nimport { isSafeDownloadUrl } from \"./artifact-download-button/artifact-download-button\";\nimport type { ArtifactDataRow } from \"./types\";\n\n/**\n * Discriminator for the result returned by `useRowsArtifactData`.\n *\n * - `loading` — fetch is in flight (or about to be).\n * - `ready` — payload fetched and parsed successfully.\n * - `error` — fetch or parse failed at runtime; user can retry.\n * - `unavailable` — the artifact has no presigned URL at all (permanent server-side state). Distinct\n * from `error` so the renderer can surface \"data is unavailable\" instead of a misleading \"try again\".\n */\nexport type RowsArtifactDataState =\n | { status: \"loading\"; rows: null; error: null }\n | { status: \"ready\"; rows: ArtifactDataRow[]; error: null }\n | { status: \"error\"; rows: null; error: Error }\n | { status: \"unavailable\"; rows: null; error: null };\n\nconst LOADING: RowsArtifactDataState = { status: \"loading\", rows: null, error: null };\nconst UNAVAILABLE: RowsArtifactDataState = { status: \"unavailable\", rows: null, error: null };\n\ninterface InternalState {\n /** The cache key the current `data` was fetched for; used to ignore stale data when inputs change. */\n cacheKey: string | null;\n /** The fetch result for `cacheKey`. */\n data: RowsArtifactDataState;\n}\n\nconst INITIAL_INTERNAL: InternalState = { cacheKey: null, data: LOADING };\n\n/** Minimum shape a column descriptor must carry for CSV projection. `TableColumn` and the\n * shapes derived from `ChartConfig` both satisfy this. */\nexport interface ArtifactColumnLike {\n key: string;\n}\n\n/** Builds a stable fingerprint for a columns array so the effect only re-runs on real changes. */\nfunction columnsFingerprint(columns: ArtifactColumnLike[]): string {\n return columns.map((c) => c.key).join(\"|\");\n}\n\n/**\n * Strips any query parameters from a URL before logging. Presigned URLs carry the AWS signature\n * in the query string, which is a time-limited bearer credential. Logging it to an observability\n * platform (Logfire/Datadog/etc.) is a credential leak.\n */\nfunction scrubUrl(url: string): string {\n const q = url.indexOf(\"?\");\n return q === -1 ? url : `${url.slice(0, q)}?<redacted>`;\n}\n\n/**\n * Fetches an artifact's CSV payload from a presigned URL and parses it via the caller-supplied\n * `parse` function. Shared core for every artifact kind whose data wire is a CSV file\n * (`TABLE`, `CHART`, …). The two type-specific renderers wrap this with a default `parse`.\n *\n * Returns `loading` while the request is in flight, `unavailable` synchronously when no URL is\n * supplied (or the URL fails the safe-scheme guard), `ready` on success, or `error` on a runtime\n * failure (HTTP non-2xx, network, or parse error). Parse errors are surfaced as `error` state —\n * they are never thrown.\n *\n * In-flight requests are cancelled via `AbortController` when the URL or columns change, or the\n * consumer unmounts, so abandoned loads don't keep consuming bandwidth.\n *\n * @param presignedUrl S3 presigned URL for the artifact's CSV file, or `null`/`undefined` when absent.\n * Validated against `isSafeDownloadUrl` before fetch — any non-http(s) scheme\n * (e.g. `file:`, `data:`, `javascript:`) is treated as `unavailable`. The same\n * guard backs `ArtifactDownloadButton`; both code paths share one check.\n * @param columns Column descriptors — used for the cache-key fingerprint AND passed verbatim to `parse`.\n * @param parse CSV-text → rows parser. **MUST be a stable (module-scope or memoised)\n * function reference** — it is intentionally omitted from the effect's dep\n * array. Passing an inline arrow function will silently skip re-parsing on\n * change. Both current call sites (`useTableArtifactData`, `useChartArtifactData`)\n * pass the module-scope `parseCsv`.\n */\nexport function useRowsArtifactData<TColumn extends ArtifactColumnLike>(\n presignedUrl: string | null | undefined,\n columns: TColumn[],\n parse: (text: string, columns: TColumn[]) => ArtifactDataRow[]\n): RowsArtifactDataState {\n // Stable fingerprint so the effect doesn't re-fire on referentially-new but content-equal column arrays.\n const colsKey = useMemo(() => columnsFingerprint(columns), [columns]);\n\n const safeUrl = presignedUrl && isSafeDownloadUrl(presignedUrl) ? presignedUrl : null;\n const cacheKey = safeUrl ? `${safeUrl}::${colsKey}` : null;\n\n const [state, setState] = useState<InternalState>(INITIAL_INTERNAL);\n\n useEffect(() => {\n if (!safeUrl) return;\n\n const controller = new AbortController();\n\n fetch(safeUrl, {\n signal: controller.signal,\n headers: { Accept: \"text/csv\" },\n // Presigned URLs are cross-origin S3 in production and don't need credentials. Explicit\n // omit so a same-origin URL (compromised payload, local dev) can't accidentally receive\n // session cookies or auth headers.\n credentials: \"omit\",\n })\n .then(async (response) => {\n if (!response.ok) throw new Error(`Failed to load artifact data (${response.status})`);\n return response.text();\n })\n .then((text) => {\n if (controller.signal.aborted) return;\n try {\n const rows = parse(text, columns);\n setState({ cacheKey: `${safeUrl}::${colsKey}`, data: { status: \"ready\", rows, error: null } });\n } catch (parseError: unknown) {\n const err = parseError instanceof Error ? parseError : new Error(String(parseError));\n console.error(\"[ArtifactData] CSV parse failed:\", err.message);\n setState({ cacheKey: `${safeUrl}::${colsKey}`, data: { status: \"error\", rows: null, error: err } });\n }\n })\n .catch((error: unknown) => {\n // Per the Fetch spec, `signal.aborted` is set before the promise rejects on abort, so this\n // guard catches every abort case driven by our `controller.abort()` cleanup.\n if (controller.signal.aborted) return;\n const err = error instanceof Error ? error : new Error(String(error));\n // Log only `err.message`, not the full Error — some browsers include the full URL (with\n // AWS signature query params) in `TypeError.message` on network errors. Belt-and-suspenders:\n // scrub query params from the URL when we log the cache key for debugging.\n console.error(`[ArtifactData] Fetch failed [${scrubUrl(safeUrl)}]:`, err.message);\n setState({ cacheKey: `${safeUrl}::${colsKey}`, data: { status: \"error\", rows: null, error: err } });\n });\n\n return () => {\n controller.abort();\n };\n // `columns` and `parse` are intentionally omitted from the dep array: `colsKey` is the stable\n // fingerprint covering every observable column change, and `parse` is required to be a stable\n // module-scope or memoised reference (see JSDoc on the `parse` param). If either becomes\n // dynamic, this assumption breaks and the suppression must be removed.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [safeUrl, colsKey]);\n\n if (!safeUrl) return UNAVAILABLE;\n // While the in-flight URL/columns change, cached data belongs to a stale key — return loading until\n // the new fetch resolves. Avoids a synchronous setState() in the effect body (lint forbids that).\n if (state.cacheKey !== cacheKey) return LOADING;\n return state.data;\n}\n","import type { ChartVariant } from \"@/components/chart/types\";\nimport { parseCsv } from \"../parse-csv\";\nimport { useRowsArtifactData, type RowsArtifactDataState } from \"../use-rows-artifact-data\";\nimport { ChartVariantServices } from \"./chart-variant-services\";\nimport type { ChartConfig } from \"./types\";\n\n/**\n * Fetches a chart artifact's data payload from a presigned URL and parses it as CSV.\n *\n * Thin wrapper over the shared `useRowsArtifactData` core. Derives the column-key list from\n * `config` by delegating to the matching variant service's `columnKeys` method — each variant\n * owns its own CSV-column projection. The keys drive `parseCsv`'s per-row projection (matches\n * the table-artifact projection contract).\n *\n * @param presignedUrl S3 presigned URL for the artifact's CSV file, or `null`/`undefined` when absent.\n * @param config Validated chart config — used to derive the projection columns.\n */\nexport function useChartArtifactData(\n presignedUrl: string | null | undefined,\n config: ChartConfig\n): RowsArtifactDataState {\n const service = ChartVariantServices[config.variant as ChartVariant];\n // `isValidChartConfig` is the caller-side guard — service should always exist here, but\n // fall back to an empty projection rather than crashing if a future invariant slips.\n const keys = service ? service.columnKeys(config) : [];\n const columns = keys.map((key) => ({ key }));\n return useRowsArtifactData(presignedUrl, columns, parseCsv);\n}\n","import type { ChartVariant } from \"@/components/chart/types\";\nimport { ChartVariantServices } from \"./chart-variant-services\";\nimport type { ChartConfig } from \"./types\";\n\n/**\n * Returns true iff `config` carries the minimum-viable shape for rendering its `variant`.\n *\n * Thin dispatcher: validates the variant discriminator is one we know, then delegates to the\n * variant service's own `isValidConfig`. Each variant owns its own validation rules — see\n * `chart-variant-services/<variant>-chart-variant-service.ts`.\n *\n * The server-side `normaliseConfig` is the authoritative validator; this predicate exists so a\n * malformed payload renders the unavailable-state instead of crashing.\n */\nexport function isValidChartConfig(config: unknown): config is ChartConfig {\n if (!config || typeof config !== \"object\") return false;\n const variant = (config as Record<string, unknown>).variant;\n if (typeof variant !== \"string\") return false;\n const service = ChartVariantServices[variant as ChartVariant];\n if (!service) return false;\n return service.isValidConfig(config);\n}\n","import { useEffect, useMemo, useState, type ReactNode } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { IconArrowsMaximize, IconArrowsMinimize } from \"@tabler/icons-react\";\n\nimport {\n Artifact,\n ArtifactActions,\n ArtifactContent,\n ArtifactHeader,\n ArtifactTitle,\n} from \"@/components/ai-elements/artifact\";\nimport { CAPTURE_EXCLUDE_ATTRIBUTE } from \"@/capture/capture-exclude-attribute\";\nimport {\n ArtifactDownloadButton,\n isSafeDownloadUrl,\n} from \"@/components/artifacts/artifact-download-button/artifact-download-button\";\nimport {\n DEFAULT_CHART_ARTIFACT_LABELS,\n DEFAULT_DECK_ARTIFACT_LABELS,\n DEFAULT_TABLE_ARTIFACT_LABELS,\n type ArtifactServiceProps,\n type DeckArtifactLabels,\n type DeckArtifactRecord,\n} from \"@/components/artifacts/types\";\nimport { IconButton } from \"@/components/icon-button/icon-button\";\nimport { DeckArtifactContent } from \"./deck-artifact-content\";\nimport { StatusMessage } from \"../status-message\";\nimport { isValidDeckConfig } from \"./validate\";\n\nconst TITLE_CLASSES = \"text-sm font-semibold text-foreground\";\n\n/**\n * Orchestrator for the `DECK`-typed artifact.\n *\n * Composes the ai-elements chrome primitives (`Artifact`, `ArtifactHeader`, `ArtifactTitle`,\n * `ArtifactActions`, `ArtifactContent`) directly around the slide navigator (`DeckArtifactContent`).\n *\n * Render and download are two independent paths. Slides render inline from `artifact.config` after\n * validation — no fetch. Download, when available, points at a `.pptx` export via\n * `protectedAsset.presignedUrl`; the button is gated on a *safe* `http(s)` URL (mirrors\n * `TableArtifact` / `ChartArtifact`).\n *\n * Fullscreen: clicking the expand button opens a React portal overlay (`createPortal` to\n * `document.body`) at 100vw×100vh. Portal is used instead of the browser Fullscreen API because\n * the chat may run in a sandboxed iframe where `requestFullscreen` is blocked.\n */\nexport function DeckArtifact({ artifact, labels, callbacks }: ArtifactServiceProps<DeckArtifactRecord>): ReactNode {\n const validConfig = isValidDeckConfig(artifact.config);\n const [isFullscreen, setIsFullscreen] = useState(false);\n // Slide index is lifted here (not owned by DeckArtifactContent) so the inline card and the\n // fullscreen overlay render the SAME slide — entering/exiting fullscreen keeps the position.\n const [currentSlide, setCurrentSlide] = useState(0);\n\n useEffect(() => {\n if (validConfig) return;\n console.warn(\"[clay] DeckArtifact: malformed `config` — skipping render.\");\n }, [validConfig]);\n\n // Close fullscreen on Escape, and lock body scroll while the overlay is open so the chat\n // underneath doesn't scroll behind it (notably on iOS/touch, where a `fixed inset-0` overlay\n // doesn't block scroll of the container beneath).\n useEffect(() => {\n if (!isFullscreen) return;\n const handleKey = (e: KeyboardEvent) => {\n if (e.key === \"Escape\") setIsFullscreen(false);\n };\n document.addEventListener(\"keydown\", handleKey);\n const previousOverflow = document.documentElement.style.overflow;\n document.documentElement.style.overflow = \"hidden\";\n return () => {\n document.removeEventListener(\"keydown\", handleKey);\n document.documentElement.style.overflow = previousOverflow;\n };\n }, [isFullscreen]);\n\n const resolvedLabels = useMemo<DeckArtifactLabels>(\n () => ({\n ...DEFAULT_DECK_ARTIFACT_LABELS,\n ...(labels ?? {}),\n chart: { ...DEFAULT_CHART_ARTIFACT_LABELS, ...(labels?.chart ?? {}) },\n table: { ...DEFAULT_TABLE_ARTIFACT_LABELS, ...(labels?.table ?? {}) },\n }),\n [labels]\n );\n\n const onNavigate = callbacks?.onNavigate;\n const handleNavigate = onNavigate ? (slideIndex: number) => onNavigate(artifact, slideIndex) : undefined;\n\n const onDownload = callbacks?.onDownload;\n const handleDownload = onDownload ? () => onDownload(artifact) : undefined;\n\n const presignedUrl = artifact.protectedAsset?.presignedUrl ?? null;\n const showDownload = presignedUrl !== null && isSafeDownloadUrl(presignedUrl);\n\n const meta = artifact.config?.meta;\n\n // Plain arrows: `setIsFullscreen` is a stable React state-setter and these close over nothing,\n // so `useCallback` would only add allocation overhead with no identity benefit (cf. handleNavigate\n // / handleDownload above, which close over `artifact` and are intentionally not memoised either).\n const handleOpenFullscreen = () => setIsFullscreen(true);\n const handleCloseFullscreen = () => setIsFullscreen(false);\n\n const actions = (\n <ArtifactActions {...{ [CAPTURE_EXCLUDE_ATTRIBUTE]: \"\" }}>\n {showDownload ? (\n <ArtifactDownloadButton href={presignedUrl ?? \"\"} label={resolvedLabels.download} onClick={handleDownload} />\n ) : null}\n {/* Fullscreen only makes sense for a renderable deck — hide the button when the config is\n invalid (the body shows the \"unavailable\" message instead of a navigator to expand). */}\n {validConfig ? (\n <IconButton\n icon={IconArrowsMaximize}\n size=\"small\"\n aria-label={resolvedLabels.enterFullscreen}\n onClick={handleOpenFullscreen}\n />\n ) : null}\n </ArtifactActions>\n );\n\n if (!validConfig) {\n return (\n <Artifact className=\"shadow-none w-full rounded-2xl bg-background/70\">\n <ArtifactHeader className=\"bg-background/70\">\n {artifact.title ? <ArtifactTitle className={TITLE_CLASSES}>{artifact.title}</ArtifactTitle> : <span />}\n {actions}\n </ArtifactHeader>\n <ArtifactContent className=\"p-0\">\n <StatusMessage $tone=\"info\">{resolvedLabels.unavailable}</StatusMessage>\n </ArtifactContent>\n </Artifact>\n );\n }\n\n return (\n <>\n <Artifact className=\"shadow-none w-full rounded-2xl bg-background/70\">\n <ArtifactHeader className=\"bg-background/70\">\n {artifact.title ? <ArtifactTitle className={TITLE_CLASSES}>{artifact.title}</ArtifactTitle> : <span />}\n {actions}\n </ArtifactHeader>\n <ArtifactContent className=\"p-0\">\n <DeckArtifactContent\n slides={artifact.config.slides}\n labels={resolvedLabels}\n onNavigate={handleNavigate}\n meta={meta}\n currentSlide={currentSlide}\n onSlideChange={setCurrentSlide}\n />\n </ArtifactContent>\n </Artifact>\n {isFullscreen &&\n createPortal(\n <FullscreenOverlay\n artifact={artifact}\n resolvedLabels={resolvedLabels}\n onNavigate={handleNavigate}\n currentSlide={currentSlide}\n onSlideChange={setCurrentSlide}\n onClose={handleCloseFullscreen}\n />,\n document.body\n )}\n </>\n );\n}\n\n/** Props for the fullscreen portal overlay. */\ninterface FullscreenOverlayProps {\n /** The deck artifact. */\n artifact: DeckArtifactRecord;\n /** Resolved labels. */\n resolvedLabels: DeckArtifactLabels;\n /** Slide navigation callback. */\n onNavigate?: (slideIndex: number) => void;\n /** Shared current slide index (controlled by the parent so it matches the inline card). */\n currentSlide: number;\n /** Shared slide-index setter. */\n onSlideChange: (slideIndex: number) => void;\n /** Close handler. */\n onClose: () => void;\n}\n\n/**\n * Fullscreen portal overlay: dark backdrop, deck centered at max 16:9, working nav, close button.\n * Rendered via `createPortal` to `document.body` instead of the browser Fullscreen API so it\n * works inside sandboxed iframes.\n */\nfunction FullscreenOverlay({\n artifact,\n resolvedLabels,\n onNavigate,\n currentSlide,\n onSlideChange,\n onClose,\n}: FullscreenOverlayProps): ReactNode {\n const meta = artifact.config?.meta;\n return (\n <div\n // Max 32-bit z-index so the overlay clears the consumer's highest layer. io-client raises its\n // chat/modal stacking (global.css sets `--z-popover: 9999999`), so a modest z-index would\n // render the overlay *behind* the chat. A full-screen modal must sit above everything.\n className=\"fixed inset-0 z-[2147483647] flex items-center justify-center bg-black/80\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label={resolvedLabels.exitFullscreen}\n >\n {/*\n Size the deck to the largest 16:9 box that fits the viewport. The slide is width-driven\n (`SlideFrame` is `aspect-[16/9] w-full`), so we set the container width to the smaller of\n 94vw and the width whose derived height (×9/16) is ~82vh — leaving ~18vh for the nav row,\n exit button, and breathing room. Without this the slide is capped at `max-w-5xl` (1024px)\n and floats tiny on large screens.\n */}\n <div className=\"relative\" style={{ width: \"min(94vw, calc(82vh * 16 / 9))\" }}>\n <DeckArtifactContent\n slides={artifact.config.slides}\n labels={resolvedLabels}\n onNavigate={onNavigate}\n meta={meta}\n currentSlide={currentSlide}\n onSlideChange={onSlideChange}\n onDark\n />\n <button\n className=\"absolute -top-10 right-6 flex items-center gap-1.5 text-sm text-white/80 hover:text-white\"\n aria-label={resolvedLabels.exitFullscreen}\n onClick={onClose}\n >\n <IconArrowsMinimize size={16} />\n <span>{resolvedLabels.exitFullscreen}</span>\n </button>\n </div>\n </div>\n );\n}\n","import { IconChevronLeft, IconChevronRight } from \"@tabler/icons-react\";\nimport { useCallback, useLayoutEffect, useRef, useState, type ReactNode } from \"react\";\n\nimport { IconButton } from \"@/components/icon-button/icon-button\";\nimport {\n DEFAULT_CHART_ARTIFACT_LABELS,\n DEFAULT_TABLE_ARTIFACT_LABELS,\n deckBlockTypes,\n deckSlideKinds,\n type DeckArtifactLabels,\n type DeckBlock,\n type DeckMeta,\n type DeckSlide,\n} from \"@/components/artifacts/types\";\nimport { ChartArtifactContent } from \"../chart-artifact/chart-artifact-content\";\nimport { StatusMessage } from \"../status-message\";\nimport { TableArtifactContent } from \"../table-artifact/table-artifact-content\";\nimport { DeckMarkdown } from \"./deck-markdown\";\nimport { AgendaSlide, ClosingSlide, SectionSlide, SlideFrame, TitleSlide } from \"./slide-renderers\";\n\n// Heights are in design-canvas px (the slide is authored at 1280×720, then scaled to fit — see\n// slide-renderers.tsx). The content area below the eyebrow row is a FIXED 636px (720 − 84) of\n// design pixels: because the canvas is only visually transformed (scale), layout pixels inside it\n// equal design pixels, so a CONTENT slide can let flexbox own the vertical space and have a visual\n// MEASURE its resolved box — no fixed chart height, no clipping, no dead whitespace.\nconst TABLE_BLOCK_MAX_SHIMMER_ROWS = 10;\n/**\n * Vertical chrome a table draws ABOVE its scroll body (header row + a little breathing room). The\n * measured zone height feeds `maxBodyHeight`, which caps only the scroll body — reserve the chrome\n * so the whole table still fits its flex box instead of overflowing it.\n */\nconst TABLE_CHROME_RESERVE = 56;\n/**\n * Seed height for a visual before it has measured its flex box. In a real browser `useLayoutEffect`\n * overwrites this with the resolved box height BEFORE the first paint (React flushes layout-effect\n * state synchronously), so it's never seen on screen — it only matters in jsdom (no layout engine,\n * `clientHeight` is 0), where it keeps the chart/table content rendered for content-presence tests.\n */\nconst VISUAL_FALLBACK_HEIGHT = 360;\n\n/** Props for the deck body — the slide navigator. */\ninterface DeckArtifactContentProps {\n /** Ordered, non-empty slide list (the orchestrator guards emptiness before rendering this). */\n slides: DeckSlide[];\n /** Resolved (defaults-merged) labels for navigation controls, position indicator, and nested blocks. */\n labels: DeckArtifactLabels;\n /** Fires on slide change with the zero-based destination index. */\n onNavigate?: (slideIndex: number) => void;\n /** Optional deck-level metadata (eyebrow, logo, date, theme). */\n meta?: DeckMeta;\n /**\n * Render the nav controls for a dark backdrop (fullscreen overlay). Default `false` (inline card).\n * When `true`, chevrons are white with a translucent-light hover (so they stay visible on the dark\n * overlay — the default dark `secondary` icon is invisible until hover otherwise).\n */\n onDark?: boolean;\n /**\n * Controlled current slide index. When provided (with `onSlideChange`), the navigator is fully\n * controlled by the parent — used so the inline card and the fullscreen overlay share one position\n * (entering/exiting fullscreen keeps the current slide). Omit both for the uncontrolled default\n * (internal state), used by standalone consumers / stories.\n */\n currentSlide?: number;\n /** Controlled-mode setter — called with the destination index alongside `onNavigate`. */\n onSlideChange?: (slideIndex: number) => void;\n}\n\n/**\n * The deck body — renders one slide at a time with previous / next navigation and a position\n * indicator. State is local (current slide index); slides are passed in from `config.slides`, so\n * there is no data fetch. Dispatches on `slide.kind` (default CONTENT) to themed sub-renderers.\n */\nexport function DeckArtifactContent({\n slides,\n labels,\n onNavigate,\n meta,\n onDark = false,\n currentSlide,\n onSlideChange,\n}: DeckArtifactContentProps): ReactNode {\n // On the dark fullscreen backdrop the default dark chevrons are invisible until hover; white\n // icons with a translucent-light hover keep them visible in both states.\n const navBtnClass = onDark ? \"[&_svg]:!text-white hover:!bg-white/20 disabled:opacity-40\" : undefined;\n const navBtnSize = onDark ? \"medium\" : \"small\";\n // Controlled when the parent supplies `currentSlide` (so the inline card and the fullscreen\n // overlay share one index); uncontrolled internal state otherwise.\n const isControlled = currentSlide !== undefined;\n const [internalIndex, setInternalIndex] = useState(0);\n const index = isControlled ? currentSlide : internalIndex;\n const current = Math.min(index, slides.length - 1);\n const slide = slides[current];\n\n const goTo = useCallback(\n (next: number) => {\n const clamped = Math.max(0, Math.min(next, slides.length - 1));\n if (isControlled) onSlideChange?.(clamped);\n else setInternalIndex(clamped);\n onNavigate?.(clamped);\n },\n [slides.length, onNavigate, isControlled, onSlideChange]\n );\n\n const position = labels.position.replace(\"{current}\", String(current + 1)).replace(\"{total}\", String(slides.length));\n\n return (\n <div className=\"flex flex-col gap-3 p-4\">\n <SlideDispatch slide={slide} labels={labels} meta={meta} />\n <div\n className={`flex items-center justify-between border-t pt-2 ${\n onDark ? \"border-white/20\" : \"border-brown-alpha-12\"\n }`}\n >\n <IconButton\n icon={IconChevronLeft}\n size={navBtnSize}\n className={navBtnClass}\n aria-label={labels.previous}\n disabled={current === 0}\n onClick={() => goTo(current - 1)}\n />\n <span className={`text-xs font-medium ${onDark ? \"text-white/70\" : \"text-brown-70\"}`}>{position}</span>\n <IconButton\n icon={IconChevronRight}\n size={navBtnSize}\n className={navBtnClass}\n aria-label={labels.next}\n disabled={current === slides.length - 1}\n onClick={() => goTo(current + 1)}\n />\n </div>\n </div>\n );\n}\n\n/** Props for the slide dispatcher. */\ninterface SlideDispatchProps {\n /** The current slide. */\n slide: DeckSlide;\n /** Resolved labels. */\n labels: DeckArtifactLabels;\n /** Optional deck meta. */\n meta?: DeckMeta;\n}\n\n/**\n * Dispatches rendering to the correct slide sub-renderer based on `slide.kind`.\n * Unknown / absent `kind` → CONTENT (backward-compatible default).\n */\nfunction SlideDispatch({ slide, labels, meta }: SlideDispatchProps): ReactNode {\n const kind = slide.kind ?? deckSlideKinds.CONTENT;\n switch (kind) {\n case deckSlideKinds.TITLE:\n return <TitleSlide slide={slide} meta={meta} />;\n case deckSlideKinds.AGENDA:\n return <AgendaSlide slide={slide} meta={meta} labels={labels} />;\n case deckSlideKinds.SECTION:\n return <SectionSlide slide={slide} meta={meta} />;\n case deckSlideKinds.CLOSING:\n return <ClosingSlide meta={meta} labels={labels} />;\n case deckSlideKinds.CONTENT:\n default:\n return <ContentSlide slide={slide} meta={meta} labels={labels} />;\n }\n}\n\n/** A chart or table block — the \"visual\" blocks, as opposed to TEXT. */\ntype DeckVisualBlock = Extract<DeckBlock, { type: typeof deckBlockTypes.CHART | typeof deckBlockTypes.TABLE }>;\n\n/** Narrow a block to a visual (chart/table). */\nfunction isVisualBlock(block: DeckBlock): block is DeckVisualBlock {\n return block.type === deckBlockTypes.CHART || block.type === deckBlockTypes.TABLE;\n}\n\n/** Props for the content slide. */\ninterface ContentSlideProps {\n /** The current slide. */\n slide: DeckSlide;\n /** Optional deck meta. */\n meta?: DeckMeta;\n /** Resolved labels forwarded to embedded chart/table blocks. */\n labels: DeckArtifactLabels;\n}\n\n/**\n * CONTENT slide. Picks one of three deliberate layouts so the slide always fills the fixed 636px\n * content area without clipping and never strands the visual in dead whitespace:\n *\n * - **Split** (text + exactly one visual): editorial two-column — framing text on the left (~42%),\n * the visual filling the full-height right column. The common \"one chart with a sentence of\n * context\" slide.\n * - **Stacked** (any other slide with ≥1 visual): a compact title header, then the blocks in order\n * with each visual growing (`flex-1`) to fill the remaining lower zone — the large-visual layout.\n * - **Text-only**: title + body copy, vertically centered, given room to breathe.\n *\n * Visuals are measured (not fixed-height) so a chart/table fills whatever flex space its layout\n * grants it — see `FillVisual`.\n */\nfunction ContentSlide({ slide, meta, labels }: ContentSlideProps): ReactNode {\n // `content` is required only for CONTENT slides, but SlideDispatch also routes any UNKNOWN\n // `kind` here (forward-compat default), and such a slide may legitimately omit `content`.\n // Guard so a missing/`null` content never throws on `.filter`/`.map` (would break the chat).\n const content = slide.content ?? [];\n const visuals = content.filter(isVisualBlock);\n const texts = content.filter(\n (b): b is Extract<DeckBlock, { type: typeof deckBlockTypes.TEXT }> => b.type === deckBlockTypes.TEXT\n );\n\n // Split: a single visual paired with framing text reads best side-by-side.\n if (visuals.length === 1 && texts.length > 0) {\n return (\n <SlideFrame meta={meta}>\n <div className=\"flex h-full items-stretch gap-12 px-16 py-12\">\n <div className=\"flex w-[42%] shrink-0 flex-col overflow-hidden\">\n <AutoFit>\n <h3 className=\"mb-5 font-bold leading-tight text-foreground\" style={{ fontSize: 34 }}>\n {slide.title}\n </h3>\n {texts.map((block, i) => (\n <DeckMarkdown key={i} markdown={block.markdown} />\n ))}\n </AutoFit>\n </div>\n <div className=\"min-h-0 min-w-0 flex-1\">\n <FillVisual block={visuals[0]} labels={labels} />\n </div>\n </div>\n </SlideFrame>\n );\n }\n\n // Stacked: compact header, then blocks in order — visuals expand to fill the lower zone.\n if (visuals.length > 0) {\n return (\n <SlideFrame meta={meta}>\n <div className=\"flex h-full flex-col gap-5 px-16 py-10\">\n <h3 className=\"shrink-0 font-bold leading-tight text-foreground\" style={{ fontSize: 32 }}>\n {slide.title}\n </h3>\n {content.map((block, i) =>\n isVisualBlock(block) ? (\n <div key={i} className=\"min-h-0 min-w-0 flex-1\">\n <FillVisual block={block} labels={labels} />\n </div>\n ) : (\n <div key={i} className=\"shrink-0 overflow-hidden\">\n <DeckMarkdown markdown={block.markdown} />\n </div>\n )\n )}\n </div>\n </SlideFrame>\n );\n }\n\n // Text-only: title + body, vertically centered with room to breathe.\n return (\n <SlideFrame meta={meta}>\n <div className=\"h-full px-24 py-12\">\n <AutoFit>\n <h3 className=\"mb-6 font-bold leading-tight text-foreground\" style={{ fontSize: 38 }}>\n {slide.title}\n </h3>\n {texts.map((block, i) => (\n <DeckMarkdown key={i} markdown={block.markdown} />\n ))}\n </AutoFit>\n </div>\n </SlideFrame>\n );\n}\n\n/** Props for the shrink-to-fit text wrapper. */\ninterface AutoFitProps {\n /** Slide text content (heading + markdown blocks) to fit within the bounded zone. */\n children: ReactNode;\n}\n\n/**\n * Shrink-to-fit wrapper for slide text. Measures the content's natural (pre-transform) height\n * against the available zone and, when the content is taller, applies a single uniform downscale so\n * it always fits the slide's fixed canvas area without clipping — the deck-tool \"shrink text on\n * overflow\" behaviour. Never scales UP (cap at 1). This is the text-side counterpart to\n * `FillVisual`: both measure the resolved box (layout px == design px on the unscaled canvas) and\n * size to it rather than guessing a fixed height.\n *\n * The scaled block is wrapped in a spacer reserving its POST-scale height so the outer\n * `justify-center` centres the *visible* (scaled) text, not the unscaled overflowing box —\n * transforms don't affect layout, so without the spacer flexbox would centre the natural height and\n * push the top of the text out of view. ResizeObserver reports pre-transform boxes, so the scale\n * write-back never feeds back into the measurement (no oscillation). In jsdom (no layout engine,\n * `clientHeight`/`offsetHeight` are 0) the scale stays 1, so tests render the text untransformed.\n */\nfunction AutoFit({ children }: AutoFitProps): ReactNode {\n const boxRef = useRef<HTMLDivElement>(null);\n const contentRef = useRef<HTMLDivElement>(null);\n const [fit, setFit] = useState({ scale: 1, height: 0 });\n\n useLayoutEffect(() => {\n const boxEl = boxRef.current;\n const contentEl = contentRef.current;\n if (!boxEl || !contentEl) return;\n const measure = (): void => {\n const avail = boxEl.clientHeight;\n const natural = contentEl.offsetHeight;\n const scale = avail > 0 && natural > avail ? avail / natural : 1;\n setFit({ scale, height: natural * scale });\n };\n measure();\n const ro = new ResizeObserver(measure);\n ro.observe(boxEl);\n ro.observe(contentEl);\n return () => ro.disconnect();\n // Set up once: the ResizeObserver already re-measures on any content/box resize (including\n // children changes that alter the natural height), so re-subscribing on every `children`\n // identity change would only tear down + re-attach the observer for no behavioural gain.\n }, []);\n\n const scaled = fit.scale < 1;\n return (\n <div ref={boxRef} className=\"flex h-full min-h-0 w-full flex-col justify-center overflow-hidden\">\n <div style={scaled ? { height: fit.height } : undefined}>\n <div\n ref={contentRef}\n className=\"w-full origin-top-left\"\n style={scaled ? { transform: `scale(${fit.scale})` } : undefined}\n >\n {children}\n </div>\n </div>\n </div>\n );\n}\n\n/** Props for the measured visual renderer. */\ninterface FillVisualProps {\n /** The chart or table block to render. */\n block: DeckVisualBlock;\n /** Resolved deck labels; nested `chart` / `table` bags are forwarded to the embedded renderer. */\n labels: DeckArtifactLabels;\n}\n\n/**\n * Renders a chart/table sized to fill its parent flex box. recharts' `ResponsiveContainer` renders\n * blank when given `height: 100%` inside a flex parent (a known clay gotcha), so we measure the\n * resolved box height (layout px == design px on the unscaled canvas) and hand the chart an\n * explicit numeric height. Tables get the measured height as `maxBodyHeight` (minus header chrome)\n * so a long roster scrolls inside its zone instead of overflowing the slide.\n */\nfunction FillVisual({ block, labels }: FillVisualProps): ReactNode {\n const ref = useRef<HTMLDivElement>(null);\n const [height, setHeight] = useState(VISUAL_FALLBACK_HEIGHT);\n\n useLayoutEffect(() => {\n const el = ref.current;\n if (!el) return;\n const apply = (h: number): void => {\n if (h > 0) setHeight(h);\n };\n apply(el.clientHeight);\n const ro = new ResizeObserver((entries) => apply(entries[0]?.contentRect.height ?? 0));\n ro.observe(el);\n return () => ro.disconnect();\n }, []);\n\n const chartLabels = labels.chart ?? DEFAULT_CHART_ARTIFACT_LABELS;\n return (\n <div ref={ref} className=\"h-full min-h-0 w-full overflow-hidden\">\n {block.type === deckBlockTypes.CHART ? (\n // ChartArtifactContent assumes a non-empty rowset (the standalone ChartArtifact guards\n // `rows.length > 0` upstream); mirror that guard here so an empty inline chart shows the\n // localized empty status instead of a blank chart area.\n block.rows.length === 0 ? (\n <StatusMessage $tone=\"info\">{chartLabels.empty}</StatusMessage>\n ) : (\n <div style={{ height }}>\n <ChartArtifactContent config={block.config} rows={block.rows} labels={chartLabels} />\n </div>\n )\n ) : (\n <TableArtifactContent\n columns={block.config.columns}\n data={{ status: \"ready\", rows: block.rows, error: null }}\n knownRowCount={block.config.stats?.rowCount ?? block.rows.length}\n maxShimmerRows={TABLE_BLOCK_MAX_SHIMMER_ROWS}\n maxBodyHeight={Math.max(0, height - TABLE_CHROME_RESERVE)}\n labels={labels.table ?? DEFAULT_TABLE_ARTIFACT_LABELS}\n />\n )}\n </div>\n );\n}\n","/**\n * Discriminant constants for `TableArtifactDataState.status`.\n *\n * Use these instead of inline string literals so a typo is a compile-time error rather than a\n * silent wrong-branch at runtime.\n */\nexport const TABLE_STATUS = {\n LOADING: \"loading\",\n READY: \"ready\",\n ERROR: \"error\",\n UNAVAILABLE: \"unavailable\",\n} as const;\n","import type { TableColumn, TableRow as TableRowData } from \"@/components/artifacts/types\";\n\n/**\n * Returns a cell's rendered string. The CSV is the source of truth — clay never coerces or\n * reformats values. `null` / missing keys render as the empty cell. The value comes out as it\n * went in (whatever the server wrote into the CSV).\n */\nexport function formatCell(row: TableRowData, column: TableColumn): string {\n return row[column.key] ?? \"\";\n}\n\n/**\n * Header + body cell class strings. Alignment is uniformly left in chat-embedded artifact tables\n * by design (mixing right-aligned numerics with left-aligned text reads inconsistently); the\n * wire-type `column.align` is preserved for forward-compat but ignored at render time. See\n * `TableColumn.align` doc in `@/components/artifacts/types`.\n */\nexport const HEAD_CLASS = \"h-auto py-2 px-4 text-xs font-medium text-foreground whitespace-nowrap text-left\";\nexport const CELL_CLASS = \"p-4 text-sm font-normal text-foreground align-top min-w-[150px] text-left\";\n\n/**\n * Row tint matches the artifact card's frosted-glass scheme. The custom `border-b-brown-30`\n * overrides shadcn's row-default border (which inherits `--color-border` = brown-40) with the\n * lighter brown-30. Hover lifts to `bg-secondary` (opaque brown-20) for strong feedback against\n * the translucent base.\n */\nexport const ROW_CLASSES = \"border-b-brown-30 bg-background/70 hover:bg-secondary\";\n\n/**\n * Mount-time fade-in for data rows so the shimmer-to-data transition feels smooth (300ms ease-in\n * over `opacity: 0 → 1`). Shimmer rows keep `ROW_CLASSES` (no animate-in) so they remain in their\n * loading stillness while the pulse animation runs. `animate-in fade-in duration-300` is provided\n * by `tw-animate-css`.\n */\nexport const DATA_ROW_CLASSES = `${ROW_CLASSES} animate-in fade-in duration-300`;\n\n/** Layered drop-shadow for the scroll-lift effect on the sticky header. */\nexport const SCROLL_SHADOW = \"0 1px 2px rgb(0 0 0 / 0.01), 0 6px 12px -2px rgb(0 0 0 / 0.03)\";\n","import { type ReactNode } from \"react\";\n\nimport { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, useScrollShadow } from \"@/components/table\";\nimport type { TableColumn, TableRow as TableRowData } from \"@/components/artifacts/types\";\nimport { StatusMessage } from \"../status-message\";\nimport type { ArtifactTableProps, TableArtifactContentProps } from \"./table-artifact-content.types\";\nimport { TABLE_STATUS } from \"./table-artifact-content.constants\";\nimport {\n CELL_CLASS,\n DATA_ROW_CLASSES,\n formatCell,\n HEAD_CLASS,\n ROW_CLASSES,\n SCROLL_SHADOW,\n} from \"./table-artifact-content.utils\";\n\n/**\n * Bridges artifact data-states → shadcn's compound `Table` primitives.\n *\n * The four states render differently:\n *\n * - **loading w/ knownRowCount** → renders a sticky-header table with N shimmer rows so the\n * layout doesn't jump.\n * - **loading w/o knownRowCount** → renders the inline \"Loading table…\" status message.\n * - **error / unavailable** → renders an inline status message.\n * - **ready** → renders the full table with data rows and the scroll-lift\n * shadow on the sticky header.\n *\n * The shadcn primitives are composed directly here — no `Table` wrapper component sits in between.\n * Each row variant (header, data, shimmer, empty) is a small inline component below.\n */\nexport function TableArtifactContent({\n columns,\n data,\n knownRowCount,\n maxShimmerRows,\n maxBodyHeight,\n labels,\n}: TableArtifactContentProps): ReactNode {\n if (data.status === TABLE_STATUS.ERROR) {\n // No retry hook today — copy intentionally drops the \"Please try again.\" that has nowhere\n // to land. Wire an `onRetry` callback into `ArtifactService` to re-mint a fresh\n // presigned URL before re-introducing the retry framing.\n return <StatusMessage $tone=\"error\">{labels.loadError}</StatusMessage>;\n }\n if (data.status === TABLE_STATUS.UNAVAILABLE) {\n return <StatusMessage $tone=\"info\">{labels.unavailable}</StatusMessage>;\n }\n if (data.status === TABLE_STATUS.LOADING) {\n if (typeof knownRowCount === \"number\" && knownRowCount > 0) {\n const shimmerCount = Math.min(knownRowCount, maxShimmerRows);\n return (\n <ArtifactTable columns={columns} maxBodyHeight={maxBodyHeight}>\n {renderShimmerRows(columns, shimmerCount)}\n </ArtifactTable>\n );\n }\n return <StatusMessage $tone=\"info\">{labels.loading}</StatusMessage>;\n }\n // data.status === TABLE_STATUS.READY\n return (\n <ArtifactTable columns={columns} maxBodyHeight={maxBodyHeight}>\n {data.rows.length === 0 ? renderEmptyRow(columns.length, labels.empty) : renderDataRows(columns, data.rows)}\n </ArtifactTable>\n );\n}\n\n/**\n * Compound-table chrome shared between the loading-shimmer branch and the data/empty branch:\n * scroll container, sticky header, and the body slot. Caller passes the body rows.\n */\nfunction ArtifactTable({ columns, maxBodyHeight, children }: ArtifactTableProps): ReactNode {\n const { targetRef, isScrolled } = useScrollShadow();\n return (\n <div ref={targetRef} className=\"overscroll-none\" style={{ maxHeight: maxBodyHeight, overflowY: \"auto\" }}>\n <Table containerClassName=\"overflow-visible\">\n <TableHeader\n data-scrolled={isScrolled ? \"true\" : undefined}\n className=\"sticky top-0 z-10 bg-background/70 backdrop-blur-lg transition-shadow\"\n style={isScrolled ? { boxShadow: SCROLL_SHADOW } : undefined}\n >\n <TableRow className=\"hover:bg-transparent\">\n {columns.map((column) => (\n <TableHead key={column.key} scope=\"col\" className={HEAD_CLASS}>\n {column.label}\n </TableHead>\n ))}\n </TableRow>\n </TableHeader>\n <TableBody>{children}</TableBody>\n </Table>\n </div>\n );\n}\n\nfunction renderShimmerRows(columns: TableColumn[], count: number): ReactNode {\n return Array.from({ length: count }).map((_, index) => (\n <TableRow key={`shimmer-${index}`} className={ROW_CLASSES} aria-hidden=\"true\">\n {columns.map((column) => (\n <TableCell key={column.key} className={CELL_CLASS}>\n <span className=\"block h-3 w-3/4 animate-pulse rounded bg-muted-foreground/20\" />\n </TableCell>\n ))}\n </TableRow>\n ));\n}\n\nfunction renderDataRows(columns: TableColumn[], rows: TableRowData[]): ReactNode {\n // `key={index}` is safe today because `rows` is a static fetch result — no sort, filter, or\n // streaming insertion. The moment row order can change (e.g. client-side sort feature), this\n // must switch to a stable per-row identity or React will reconcile incorrectly (inputs lose\n // focus, animations glitch). The data hook will need to surface a row-id at that point.\n return rows.map((row, index) => (\n <TableRow key={index} className={DATA_ROW_CLASSES}>\n {columns.map((column) => (\n <TableCell key={column.key} className={CELL_CLASS}>\n {formatCell(row, column)}\n </TableCell>\n ))}\n </TableRow>\n ));\n}\n\nfunction renderEmptyRow(colSpan: number, emptyMessage: string): ReactNode {\n return (\n <TableRow className=\"hover:bg-transparent\">\n <TableCell colSpan={colSpan} className=\"py-6 text-center text-sm text-muted-foreground\">\n {emptyMessage}\n </TableCell>\n </TableRow>\n );\n}\n","import { type ComponentProps, type ReactNode } from \"react\";\nimport ReactMarkdown from \"react-markdown\";\nimport remarkBreaks from \"remark-breaks\";\nimport remarkGfm from \"remark-gfm\";\n\nimport { isSafeDownloadUrl } from \"@/components/artifacts/artifact-download-button/artifact-download-button\";\n\n/**\n * react-markdown's element-override map. Derived from the component's own props rather than the\n * package's named `Components` export, which doesn't resolve under this repo's `bundler` module\n * resolution. Keeps per-element props strongly typed (no implicit `any` on the `node` field).\n */\ntype MarkdownComponents = NonNullable<ComponentProps<typeof ReactMarkdown>[\"components\"]>;\n\n/**\n * Props react-markdown hands a custom element renderer: the intrinsic element's own attributes\n * plus the AST `node`. Typed explicitly (rather than inferred) so the renderers below stay strict\n * — the resolved `MarkdownComponents` value type is too loose (`unknown` props) to infer from.\n */\ntype MarkdownElementProps<Tag extends keyof React.JSX.IntrinsicElements> = React.JSX.IntrinsicElements[Tag] & {\n /** Source AST node — unused by these renderers. */\n node?: unknown;\n};\n\n/** `<li>` props plus the list metadata react-markdown injects (stripped before reaching the DOM). */\ntype MarkdownLiProps = MarkdownElementProps<\"li\"> & {\n /** True when the parent list is ordered. */\n ordered?: boolean;\n /** Zero-based position within the parent list. */\n index?: number;\n /** Task-list checkbox state, or `null` for a plain item. */\n checked?: boolean | null;\n};\n\n/** Table-cell props plus react-markdown's `isHeader` flag (stripped before reaching the DOM). */\ntype MarkdownCellProps = (MarkdownElementProps<\"th\"> | MarkdownElementProps<\"td\">) & {\n /** True when the cell is a header cell. */\n isHeader?: boolean;\n};\n\n/** Props for `DeckMarkdown`. */\ninterface DeckMarkdownProps {\n /** Markdown source for a slide's text block. */\n markdown: string;\n}\n\n/**\n * Strips io-server's `<!-- table-title: ... -->` HTML-comment metadata before rendering — the\n * marker is emitted by the server's CSV-export pipeline and has no place in visual output.\n * Replicated here (rather than imported from `blocks/text-block`) because the artifact layer must\n * not depend on the block layer: `@shapesos/clay/blocks` already imports from\n * `@shapesos/clay/artifacts`, so the reverse edge would form an entry-point cycle. Keep this\n * contract aligned with `text-block/sanitize.ts`.\n */\nfunction 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\n/**\n * Element → Tailwind-class mapping. The artifact layer is Tailwind-styled (clay has no\n * `@tailwindcss/typography` `prose` plugin), so each markdown element gets explicit utilities\n * rather than inheriting from a styled-components wrapper — this is what fixes the v1 bug where\n * `**bold**` rendered literally (the old renderer used `whitespace-pre-wrap` over raw text).\n */\n// Sizes are tuned for the deck's fixed 1280×720 design canvas (see `slide-renderers.tsx`), not the\n// chat text-block — the whole slide is uniformly scaled afterward, so these absolute sizes are what\n// keep slide body copy legibly proportioned at canvas scale (≈22px body, ≈30px headings).\nconst MARKDOWN_COMPONENTS = {\n h1: ({ node: _node, ...props }: MarkdownElementProps<\"h1\">) => (\n <h1 className=\"text-[30px] font-semibold leading-tight text-foreground mb-3\" {...props} />\n ),\n h2: ({ node: _node, ...props }: MarkdownElementProps<\"h2\">) => (\n <h2 className=\"text-[26px] font-semibold leading-tight text-foreground mb-2\" {...props} />\n ),\n h3: ({ node: _node, ...props }: MarkdownElementProps<\"h3\">) => (\n <h3 className=\"text-[24px] font-semibold leading-tight text-foreground mb-2\" {...props} />\n ),\n h4: ({ node: _node, ...props }: MarkdownElementProps<\"h4\">) => (\n <h4 className=\"text-[24px] font-semibold leading-tight text-foreground mb-2\" {...props} />\n ),\n h5: ({ node: _node, ...props }: MarkdownElementProps<\"h5\">) => (\n <h5 className=\"text-[24px] font-semibold leading-tight text-foreground mb-2\" {...props} />\n ),\n h6: ({ node: _node, ...props }: MarkdownElementProps<\"h6\">) => (\n <h6 className=\"text-[24px] font-semibold leading-tight text-foreground mb-2\" {...props} />\n ),\n p: ({ node: _node, ...props }: MarkdownElementProps<\"p\">) => (\n <p className=\"text-[22px] leading-relaxed text-foreground my-2\" {...props} />\n ),\n ul: ({ node: _node, ...props }: MarkdownElementProps<\"ul\">) => (\n <ul className=\"list-disc pl-7 text-[22px] leading-relaxed text-foreground my-2\" {...props} />\n ),\n ol: ({ node: _node, ...props }: MarkdownElementProps<\"ol\">) => (\n <ol className=\"list-decimal pl-7 text-[22px] leading-relaxed text-foreground my-2\" {...props} />\n ),\n // react-markdown injects list metadata (`ordered` / `index` / `checked`) that must not leak to the DOM.\n li: ({ node: _node, ordered: _ordered, index: _index, checked: _checked, ...props }: MarkdownLiProps) => (\n <li className=\"my-1\" {...props} />\n ),\n strong: ({ node: _node, ...props }: MarkdownElementProps<\"strong\">) => (\n <strong className=\"font-semibold\" {...props} />\n ),\n em: ({ node: _node, ...props }: MarkdownElementProps<\"em\">) => <em className=\"italic\" {...props} />,\n // Gate `<img src>` against an http(s) allowlist. react-markdown v8 percent-encodes the URL but\n // does NOT protocol-filter image src (unlike link href), so an LLM-authored\n // `![](javascript:…)` / `![](data:…)` or a tracking-pixel `![](http://…)` would otherwise render\n // with the raw URL. Unsafe src → render nothing.\n img: ({ node: _node, src, ...props }: MarkdownElementProps<\"img\">) =>\n typeof src === \"string\" && isSafeDownloadUrl(src) ? <img src={src} className=\"max-w-full\" {...props} /> : null,\n a: ({ node: _node, ...props }: MarkdownElementProps<\"a\">) => (\n // `rel` is forced AFTER `{...props}` so it can't be overridden — if a consumer ever adds\n // `linkTarget=\"_blank\"`, LLM-authored links still can't reach `window.opener`.\n <a className=\"underline underline-offset-2\" {...props} rel=\"noopener noreferrer\" />\n ),\n // react-markdown passes `inline` to distinguish inline code from fenced blocks — strip it from the DOM.\n code: ({ node: _node, inline: _inline, ...props }: MarkdownElementProps<\"code\"> & { inline?: boolean }) => (\n <code className=\"rounded bg-muted px-1.5 py-0.5 font-mono text-[18px]\" {...props} />\n ),\n blockquote: ({ node: _node, ...props }: MarkdownElementProps<\"blockquote\">) => (\n <blockquote className=\"border-l-2 border-brown-alpha-12 pl-4 text-[22px] text-muted-foreground\" {...props} />\n ),\n hr: ({ node: _node, ...props }: MarkdownElementProps<\"hr\">) => (\n <hr className=\"my-4 border-brown-alpha-12\" {...props} />\n ),\n table: ({ node: _node, ...props }: MarkdownElementProps<\"table\">) => (\n <div className=\"overflow-x-auto\">\n <table className=\"w-full border-collapse text-[20px]\" {...props} />\n </div>\n ),\n // react-markdown passes `isHeader` to cell renderers — strip it so it never reaches the DOM.\n th: ({ node: _node, isHeader: _isHeader, ...props }: MarkdownCellProps) => (\n <th className=\"border-b border-brown-alpha-12 px-3 py-1.5 text-left font-medium\" {...props} />\n ),\n td: ({ node: _node, isHeader: _isHeader, ...props }: MarkdownCellProps) => (\n <td className=\"border-b border-brown-alpha-12 px-3 py-1.5\" {...props} />\n ),\n} as MarkdownComponents;\n\n/**\n * Renders a slide text block as Markdown with GFM (tables, strikethrough) and single-newline line\n * breaks — the same react-markdown pipeline as the chat `TextBlock`, so `**bold**`, lists, and\n * headings render as formatted output rather than literal syntax.\n */\nexport function DeckMarkdown({ markdown }: DeckMarkdownProps): ReactNode {\n // XSS posture for this agent-authored markdown: react-markdown v8 does NOT render raw HTML —\n // raw HTML is only parsed when the `rehype-raw` plugin is added, which we deliberately do not.\n // So embedded `<script>` / `<img onerror>` arrive as inert escaped text. This matches the\n // canonical chat `text-block` renderer. (`sanitize` only strips the server's `<!-- table-title -->`\n // marker; it is NOT the XSS guard — the absence of rehype-raw is.) Do not add rehype-raw here.\n return (\n <ReactMarkdown remarkPlugins={[remarkGfm, remarkBreaks]} components={MARKDOWN_COMPONENTS}>\n {sanitize(markdown)}\n </ReactMarkdown>\n );\n}\n","import { type ReactNode, useLayoutEffect, useRef, useState } from \"react\";\n\nimport { fontFamilies } from \"@/tokens\";\nimport type { DeckArtifactLabels, DeckMeta, DeckSlide } from \"@/components/artifacts/types\";\nimport { isSafeDownloadUrl } from \"@/components/artifacts/artifact-download-button/artifact-download-button\";\n\n/**\n * `meta.logoUrl` is client-injected from account branding (admin-controlled), so it must never reach\n * an `<img src>` unchecked — a `javascript:` / `data:` / off-origin value would be an injection /\n * tracking vector. Reuse the artifact download-button's http(s)-scheme allowlist as the render gate.\n * Returns the URL only when safe, else `null` (the logo slot then renders absent).\n */\nfunction safeLogoUrl(meta?: DeckMeta): string | null {\n return meta?.logoUrl && isSafeDownloadUrl(meta.logoUrl) ? meta.logoUrl : null;\n}\n\n// ── Design canvas ───────────────────────────────────────────────────────────\n//\n// Every slide is authored at a FIXED 1280×720 (16:9) canvas and then uniformly scaled to fit the\n// rendered frame via `transform: scale(frameWidth / 1280)`. This is the slide-editor approach: a\n// single scale factor grows/shrinks text, charts, and spacing TOGETHER, so the same deck looks\n// correct tiny (inline chat preview — whole slide visible, no scroll) and large (fullscreen).\n// Authoring at a fixed resolution is why every size below is an absolute px value, not a responsive\n// utility — responsiveness comes entirely from the outer scale.\n\nconst DESIGN_W = 1280;\nconst DESIGN_H = 720;\n/** Eyebrow row height on the design canvas; the content area starts below it. */\nconst EYEBROW_H = 84;\n/** Logo max height on the design canvas (scales with the slide). */\nconst CANVAS_LOGO_MAX_H = 48;\n\n/** Crimson Pro italic — the editorial accent face used for eyebrows, agenda numbers, pull quotes. */\nconst ACCENT_FONT = { fontFamily: `\"${fontFamilies.CRIMSON_PRO}\", serif`, fontStyle: \"italic\" } as const;\n\n// ── SlideFrame ────────────────────────────────────────────────────────────────\n\n/** Props shared by every slide frame and sub-renderer. */\ninterface SlideFrameProps {\n /** Optional deck-level meta (eyebrow, logo, date). */\n meta?: DeckMeta;\n /** Slide children rendered inside the frame's content area (below the eyebrow row). */\n children: ReactNode;\n /** When true, the frame uses a full-bleed accent background (section slides). */\n accent?: boolean;\n /**\n * When true, the frame drops its eyebrow chrome (running header + hairline + top-right logo) and\n * hands the child the FULL canvas. Used by the TITLE slide, which paints its own cover layout\n * (company logo top-left, oversized title, date) rather than the per-slide running header.\n */\n bare?: boolean;\n}\n\n/**\n * Shared wrapper rendered around EVERY slide kind. The outer box holds the 16:9 aspect ratio and is\n * sized by its parent (full width inline, large in fullscreen). Inside, a fixed 1280×720 canvas is\n * scaled by `frameWidth / 1280` so all content scales uniformly with the frame.\n *\n * On accent slides (SECTION) the background is `bg-indigo-600` and text is reversed. On `bare`\n * slides (TITLE) the eyebrow row is omitted and the content area spans the whole canvas.\n */\nexport function SlideFrame({ meta, children, accent = false, bare = false }: SlideFrameProps): ReactNode {\n const frameRef = useRef<HTMLDivElement>(null);\n // `null` until measured so the first paint already has the right scale (set in useLayoutEffect\n // before the browser paints — avoids a flash of an unscaled 1280px canvas).\n const [scale, setScale] = useState<number | null>(null);\n\n useLayoutEffect(() => {\n const el = frameRef.current;\n if (!el) return;\n const apply = (width: number) => {\n if (width > 0) setScale(width / DESIGN_W);\n };\n apply(el.clientWidth);\n const ro = new ResizeObserver((entries) => apply(entries[0]?.contentRect.width ?? 0));\n ro.observe(el);\n return () => ro.disconnect();\n }, []);\n\n const bg = accent ? \"bg-indigo-600\" : \"bg-deck-canvas\";\n const eyebrowColor = accent ? \"text-white/70\" : \"text-brown-60\";\n const borderColor = accent ? \"border-white/20\" : \"border-brown-alpha-12\";\n const logoSrc = safeLogoUrl(meta);\n\n return (\n <div ref={frameRef} className={`relative aspect-[16/9] w-full overflow-hidden rounded-lg ${bg}`}>\n <div\n className=\"absolute left-0 top-0 origin-top-left\"\n style={{\n width: DESIGN_W,\n height: DESIGN_H,\n transform: `scale(${scale ?? 0})`,\n visibility: scale === null ? \"hidden\" : \"visible\",\n }}\n >\n {/* Eyebrow row: running header (left) + account logo (right). Omitted on `bare` (TITLE). */}\n {!bare ? (\n <div\n className={`absolute inset-x-0 top-0 flex items-center justify-between border-b px-16 ${borderColor}`}\n style={{ height: EYEBROW_H }}\n >\n {meta?.eyebrow ? (\n <span className={eyebrowColor} style={{ ...ACCENT_FONT, fontSize: 22 }}>\n {meta.eyebrow}\n </span>\n ) : (\n <span />\n )}\n {logoSrc ? (\n <img src={logoSrc} alt=\"\" className=\"object-contain\" style={{ maxHeight: CANVAS_LOGO_MAX_H }} />\n ) : null}\n </div>\n ) : null}\n {/* Content area — fills the canvas (below the eyebrow row unless `bare`). */}\n <div className=\"absolute inset-x-0 bottom-0 overflow-hidden\" style={{ top: bare ? 0 : EYEBROW_H }}>\n {children}\n </div>\n </div>\n </div>\n );\n}\n\n// ── TITLE slide ────────────────────────────────────────────────────────────────\n\n/** Props for TitleSlide. */\ninterface TitleSlideProps {\n /** The slide data. */\n slide: DeckSlide;\n /** Optional deck meta for eyebrow / logo. */\n meta?: DeckMeta;\n}\n\n/**\n * TITLE / cover slide. Editorial left-aligned layout (not centered): the account's own logo lockup\n * sits top-left, an oversized Geist-bold title fills the middle band, and the date renders in\n * Crimson Pro italic below — the whole group vertically centred against the bare canvas. The logo\n * is `meta.logoUrl` (the client injects the account's `fullLogoPublicUrl` / avatar fallback); absent\n * → the title block simply starts higher. Uses `bare` so there's no running-header chrome competing\n * with the cover.\n */\nexport function TitleSlide({ slide, meta }: TitleSlideProps): ReactNode {\n const logoSrc = safeLogoUrl(meta);\n return (\n <SlideFrame meta={meta} bare>\n <div className=\"flex h-full flex-col justify-center px-24 py-16\">\n {logoSrc ? (\n <img\n src={logoSrc}\n alt=\"\"\n className=\"mb-16 object-contain object-left\"\n style={{ maxHeight: 56, maxWidth: 380 }}\n />\n ) : null}\n <h1\n className=\"font-bold tracking-tight text-brown-100\"\n style={{ fontSize: 92, lineHeight: 1.04, maxWidth: 1060 }}\n >\n {slide.title}\n </h1>\n {slide.subtitle ? (\n <p className=\"mt-7 text-brown-70\" style={{ ...ACCENT_FONT, fontSize: 34 }}>\n {slide.subtitle}\n </p>\n ) : null}\n {slide.description ? (\n <p className=\"mt-4 leading-relaxed text-brown-70\" style={{ fontSize: 24, maxWidth: 880 }}>\n {slide.description}\n </p>\n ) : null}\n {meta?.date ? (\n <p className=\"mt-10 text-brown-70\" style={{ ...ACCENT_FONT, fontSize: 30 }}>\n {meta.date}\n </p>\n ) : null}\n </div>\n </SlideFrame>\n );\n}\n\n// ── AGENDA slide ────────────────────────────────────────────────────────────────\n\n/** Props for AgendaSlide. */\ninterface AgendaSlideProps {\n /** The slide data. */\n slide: DeckSlide;\n /** Optional deck meta for eyebrow / logo. */\n meta?: DeckMeta;\n /** Resolved labels for the \"Agenda\" fallback title. */\n labels: DeckArtifactLabels;\n}\n\n/**\n * AGENDA slide: slide title (or \"Agenda\" fallback) + numbered rows `01/02/...` in italic\n * Crimson Pro accent, separated by hairline rules.\n */\nexport function AgendaSlide({ slide, meta, labels }: AgendaSlideProps): ReactNode {\n const items = slide.items ?? [];\n return (\n <SlideFrame meta={meta}>\n <div className=\"h-full px-24 py-14\">\n <h2 className=\"mb-10 font-bold text-brown-90\" style={{ fontSize: 40 }}>\n {slide.title || labels.agenda}\n </h2>\n <div className=\"flex flex-col\">\n {items.map((item, i) => (\n <div key={i} className=\"flex items-baseline gap-6 border-b border-brown-alpha-12 py-5\">\n <span className=\"shrink-0 text-indigo-600\" style={{ ...ACCENT_FONT, fontSize: 28, width: 64 }}>\n {String(i + 1).padStart(2, \"0\")}\n </span>\n <span className=\"text-brown-90\" style={{ fontSize: 28 }}>\n {item}\n </span>\n </div>\n ))}\n </div>\n </div>\n </SlideFrame>\n );\n}\n\n// ── SECTION slide ────────────────────────────────────────────────────────────────\n\n/** Props for SectionSlide. */\ninterface SectionSlideProps {\n /** The slide data. */\n slide: DeckSlide;\n /** Optional deck meta for eyebrow / logo. */\n meta?: DeckMeta;\n}\n\n/**\n * SECTION / divider slide: full-bleed indigo-600 background, reversed eyebrow + large Geist\n * title + short accent rule bottom-left.\n */\nexport function SectionSlide({ slide, meta }: SectionSlideProps): ReactNode {\n return (\n <SlideFrame meta={meta} accent>\n <div className=\"flex h-full flex-col justify-center gap-6 px-24\">\n <h2 className=\"font-bold text-white\" style={{ fontSize: 72, lineHeight: 1.05, maxWidth: 940 }}>\n {slide.title}\n </h2>\n <div className=\"mt-2 bg-white/50\" style={{ width: 96, height: 5 }} />\n </div>\n </SlideFrame>\n );\n}\n\n// ── CLOSING slide ────────────────────────────────────────────────────────────────\n\n/** Props for ClosingSlide. */\ninterface ClosingSlideProps {\n /** Optional deck meta for eyebrow / logo. */\n meta?: DeckMeta;\n /** Resolved labels for thank-you + questions text. */\n labels: DeckArtifactLabels;\n}\n\n/**\n * CLOSING slide: centered \"Thank you\" + \"Questions?\" from labels. No content blocks rendered.\n */\nexport function ClosingSlide({ meta, labels }: ClosingSlideProps): ReactNode {\n return (\n <SlideFrame meta={meta}>\n <div className=\"flex h-full flex-col items-center justify-center gap-6 px-24 text-center\">\n <h1 className=\"font-bold text-brown-100\" style={{ fontSize: 72 }}>\n {labels.thankYou}\n </h1>\n <p className=\"text-indigo-600\" style={{ ...ACCENT_FONT, fontSize: 36 }}>\n {labels.questions}\n </p>\n </div>\n </SlideFrame>\n );\n}\n","import type { TableColumn, TableConfig } from \"@/components/artifacts/types\";\n\n/**\n * Returns true iff `config` carries the minimum-viable shape for rendering: a non-empty array\n * of columns where each has a non-empty `key` and a string `label`.\n *\n * Anything else is trusted to the server. The deprecated `type` / `align` wire fields are NOT\n * validated — clay doesn't act on them, so rejecting unknown values would punish servers\n * shipping fields clay was about to ignore anyway.\n */\nexport function isValidTableConfig(config: unknown): config is TableConfig {\n if (!config || typeof config !== \"object\") return false;\n const candidate = config as { columns?: unknown };\n if (!Array.isArray(candidate.columns)) return false;\n if (candidate.columns.length === 0) return false;\n return candidate.columns.every(isValidTableColumn);\n}\n\n/** Minimum-viable column shape: non-empty string `key` + string `label`. */\nfunction isValidTableColumn(column: unknown): column is TableColumn {\n if (!column || typeof column !== \"object\") return false;\n const candidate = column as Record<string, unknown>;\n if (typeof candidate.key !== \"string\" || candidate.key.length === 0) return false;\n if (typeof candidate.label !== \"string\") return false;\n return true;\n}\n","import type { DeckBlock, DeckConfig, DeckSlide } from \"@/components/artifacts/types\";\nimport { isValidChartConfig } from \"../chart-artifact/validate\";\nimport { isValidTableConfig } from \"../table-artifact/validate\";\nimport { deckBlockTypes, deckSlideKinds } from \"./types\";\n\n/**\n * Returns true iff `config` carries the minimum-viable shape for rendering.\n */\nexport function isValidDeckConfig(config: unknown): config is DeckConfig {\n if (!config || typeof config !== \"object\") return false;\n const candidate = config as { slides?: unknown };\n if (!Array.isArray(candidate.slides)) return false;\n if (candidate.slides.length === 0) return false;\n return candidate.slides.every(isValidDeckSlide);\n}\n\n/** Minimum-viable slide shape: string `title` + an array of valid content blocks (may be empty). */\nfunction isValidDeckSlide(slide: unknown): slide is DeckSlide {\n if (!slide || typeof slide !== \"object\") return false;\n const candidate = slide as Record<string, unknown>;\n if (typeof candidate.title !== \"string\") return false;\n // `kind` is optional — any value (including unknown string) is tolerated; renderer defaults to CONTENT.\n const kind = typeof candidate.kind === \"string\" ? candidate.kind : deckSlideKinds.CONTENT;\n // Only CONTENT slides must carry a `content` array. Structural kinds\n // (TITLE/AGENDA/SECTION/CLOSING) legitimately omit blocks — the agent emits\n // `content: []`, but a missing/`null` `content` on those kinds must NOT suppress\n // the whole deck (it previously hard-failed here, taking down every slide).\n if (kind === deckSlideKinds.CONTENT && !Array.isArray(candidate.content)) return false;\n const content = Array.isArray(candidate.content) ? candidate.content : [];\n // `subtitle`, `description` are optional strings — tolerated when absent.\n // `items` must be string[] when present. `!= null` treats both `undefined` and a wire `null`\n // (GraphQL returns nullable fields as `null` on slide kinds that don't use `items`) as absent.\n if (candidate.items != null) {\n if (!Array.isArray(candidate.items)) return false;\n if (!candidate.items.every((item: unknown) => typeof item === \"string\")) return false;\n }\n return content.every(isValidDeckBlock);\n}\n\n/**\n * Minimum-viable block shape, dispatched on the `type` discriminator.\n * Unknown block kinds are rejected so the renderer's per-block fallback never receives a shape it\n * can't dispatch.\n */\nfunction isValidDeckBlock(block: unknown): block is DeckBlock {\n if (!block || typeof block !== \"object\") return false;\n const candidate = block as Record<string, unknown>;\n switch (candidate.type) {\n case deckBlockTypes.TEXT:\n return typeof candidate.markdown === \"string\";\n case deckBlockTypes.CHART:\n return isValidChartConfig(candidate.config) && Array.isArray(candidate.rows);\n case deckBlockTypes.TABLE:\n return isValidTableConfig(candidate.config) && Array.isArray(candidate.rows);\n default:\n return false;\n }\n}\n","import { useEffect, useMemo, type ReactNode } from \"react\";\n\nimport {\n Artifact,\n ArtifactActions,\n ArtifactContent,\n ArtifactHeader,\n ArtifactTitle,\n} from \"@/components/ai-elements/artifact\";\nimport { ArtifactDownloadButton } from \"@/components/artifacts/artifact-download-button/artifact-download-button\";\nimport {\n DEFAULT_TABLE_ARTIFACT_LABELS,\n type ArtifactServiceProps,\n type TableArtifactLabels,\n type TableArtifactRecord,\n} from \"@/components/artifacts/types\";\nimport { TableArtifactContent } from \"./table-artifact-content\";\nimport { StatusMessage } from \"../status-message\";\nimport { useTableArtifactData } from \"./use-table-artifact-data\";\nimport { isValidTableConfig } from \"./validate\";\n\nconst TITLE_CLASSES = \"text-sm font-semibold text-foreground\";\n\n/**\n * Cap the body at ~10 rows × 48px (~480px). Long lists scroll inside the artifact so they\n * don't stretch the chat surface. Each artifact type declares its own sensible cap.\n */\nconst TABLE_MAX_BODY_HEIGHT = 480;\n\n/**\n * Cap on shimmer rows during loading. Approximates the visible viewport (`TABLE_MAX_BODY_HEIGHT`\n * ÷ 48px per row) so we don't render hundreds of placeholder DOM nodes for very large tables.\n */\nconst MAX_SHIMMER_ROWS = 10;\n\n/**\n * Orchestrator for the `table`-typed artifact.\n *\n * Composes the ai-elements chrome primitives (`Artifact`, `ArtifactHeader`, `ArtifactTitle`,\n * `ArtifactActions`, `ArtifactContent`) directly around the table body (`TableArtifactContent`).\n * Download is handled via `ArtifactDownloadButton` — a shared utility component that applies the\n * safe-URL guard and renders nothing for non-http(s) schemes.\n */\nexport function TableArtifact({ artifact, labels, callbacks }: ArtifactServiceProps<TableArtifactRecord>): ReactNode {\n const validConfig = isValidTableConfig(artifact.config);\n const validColumns = validConfig ? artifact.config.columns : null;\n\n useEffect(() => {\n if (validConfig) return;\n console.warn(\"[clay] TableArtifact: malformed `config` — skipping render.\");\n }, [validConfig]);\n\n // Merge consumer-supplied labels over the English defaults. See `ChartArtifact` for rationale.\n const resolvedLabels = useMemo<TableArtifactLabels>(\n () => ({ ...DEFAULT_TABLE_ARTIFACT_LABELS, ...(labels ?? {}) }),\n [labels]\n );\n\n // Hook is always called (Rules of Hooks). Pass null URL when config is invalid so the hook\n // short-circuits at its `if (!presignedUrl) return UNAVAILABLE` path — no network fetch fires\n // for an artifact whose config shape can't be rendered.\n const data = useTableArtifactData(\n validConfig ? artifact.protectedAsset?.presignedUrl ?? null : null,\n validColumns ?? []\n );\n\n const presignedUrl = artifact.protectedAsset?.presignedUrl ?? null;\n\n // Bridge the consumer's `onDownload` callback into the download button. Wrapped to capture\n // `artifact` so the callback receives analytics context without a per-block closure on the\n // consumer side. `undefined` (rather than a no-op) when no callback wired — keeps the button\n // free of a redundant click listener.\n const onDownload = callbacks?.onDownload;\n const handleDownloadClick = onDownload ? () => onDownload(artifact) : undefined;\n\n if (!validConfig || !validColumns) {\n return (\n <Artifact className=\"shadow-none w-full rounded-2xl bg-background/70\">\n <ArtifactHeader className=\"bg-background/70\">\n {artifact.title ? <ArtifactTitle className={TITLE_CLASSES}>{artifact.title}</ArtifactTitle> : <span />}\n {presignedUrl ? (\n <ArtifactActions>\n <ArtifactDownloadButton\n href={presignedUrl}\n label={resolvedLabels.download}\n onClick={handleDownloadClick}\n />\n </ArtifactActions>\n ) : null}\n </ArtifactHeader>\n <ArtifactContent className=\"p-0 overflow-auto\" style={{ maxHeight: TABLE_MAX_BODY_HEIGHT }}>\n <StatusMessage $tone=\"info\">{resolvedLabels.unavailable}</StatusMessage>\n </ArtifactContent>\n </Artifact>\n );\n }\n\n return (\n // The table body owns its own scroll container (`ArtifactTable` inside `TableArtifactContent`).\n // Disable the outer overflow container so only one scroll context exists — this prevents\n // double-scroll nesting and lets `useScrollShadow` find the correct scroll ancestor.\n <Artifact className=\"shadow-none w-full rounded-2xl bg-background/70\">\n <ArtifactHeader className=\"bg-background/70\">\n {artifact.title ? <ArtifactTitle className={TITLE_CLASSES}>{artifact.title}</ArtifactTitle> : <span />}\n <ArtifactActions>\n <ArtifactDownloadButton\n href={presignedUrl ?? \"\"}\n label={resolvedLabels.download}\n onClick={handleDownloadClick}\n />\n </ArtifactActions>\n </ArtifactHeader>\n <ArtifactContent className=\"p-0 overflow-visible\" style={{ maxHeight: \"none\" }}>\n <TableArtifactContent\n columns={validColumns}\n data={data}\n knownRowCount={artifact.config.stats?.rowCount}\n maxShimmerRows={MAX_SHIMMER_ROWS}\n maxBodyHeight={TABLE_MAX_BODY_HEIGHT}\n labels={resolvedLabels}\n />\n </ArtifactContent>\n </Artifact>\n );\n}\n","import type { TableColumn } from \"@/components/artifacts/types\";\nimport { useRowsArtifactData, type RowsArtifactDataState } from \"../use-rows-artifact-data\";\nimport { parseCsv } from \"../parse-csv\";\n\n/**\n * Per-table data state. Re-exported from the shared rows-state union so callers that imported\n * the original `TableArtifactDataState` name still resolve. `TableRow` is itself an alias for\n * `ArtifactDataRow`, so `rows: TableRow[]` and `rows: ArtifactDataRow[]` are the same type.\n */\nexport type TableArtifactDataState = RowsArtifactDataState;\n\n/**\n * Fetches a table artifact's data payload from a presigned URL and parses it as CSV.\n *\n * Thin wrapper over `useRowsArtifactData` — the shared core for every artifact kind whose data\n * wire is a CSV file. Supplies `parseCsv` as the parser (per-column projection from CSV headers,\n * empty cells coerced to `null`).\n *\n * @param presignedUrl S3 presigned URL for the artifact's CSV file, or `null`/`undefined` when absent.\n * @param columns Column definitions from `TableConfig.columns` — used for projection.\n */\nexport function useTableArtifactData(\n presignedUrl: string | null | undefined,\n columns: TableColumn[]\n): RowsArtifactDataState {\n return useRowsArtifactData(presignedUrl, columns, parseCsv);\n}\n","import type { ChartArtifactRecord } from \"@/components/artifacts/types\";\n\n/**\n * Produces a plain-text clipboard representation of a chart artifact.\n *\n * Returns the artifact title as plain text so pasting into non-markdown-aware targets\n * (plain-text editors, terminal, spreadsheets) does not expose raw markdown syntax. Mirrors\n * `tableArtifactToClipboardText`'s contract — every block-aggregating consumer\n * (`ArtifactRefBlockService.toClipboardText`) writes the aggregated string verbatim.\n *\n * The presigned URL is intentionally omitted: presigned URLs expire quickly and are not useful\n * when pasted as bare text. The Download button in the artifact chrome is the canonical way\n * to obtain the underlying file.\n */\nexport function chartArtifactToClipboardText(artifact: ChartArtifactRecord): string {\n const trimmedTitle = artifact.title?.trim();\n return trimmedTitle || \"Chart\";\n}\n","import type { ArtifactService, ChartArtifactRecord } from \"../types\";\nimport { artifactTypes } from \"../types\";\nimport { ChartArtifact } from \"../chart-artifact/chart-artifact\";\nimport { chartArtifactToClipboardText } from \"../chart-artifact/to-clipboard-text\";\n\nexport const ChartArtifactService: ArtifactService<ChartArtifactRecord> = {\n type: artifactTypes.CHART,\n Component: ChartArtifact,\n toClipboardText: chartArtifactToClipboardText,\n};\n","import {\n deckBlockTypes,\n deckSlideKinds,\n type DeckArtifactRecord,\n type DeckBlock,\n type DeckSlide,\n} from \"@/components/artifacts/types\";\n\n/**\n * Produces a plain-text clipboard representation of a deck artifact.\n *\n * Decks carry their content inline in `config.slides`, so (unlike `TABLE` / `CHART`, whose data\n * lives behind an expiring presigned URL) the clipboard value can include the full deck text:\n * the title followed by each slide's title and a plain-text rendering of its content blocks.\n *\n * Text blocks contribute their markdown verbatim; chart / table blocks contribute a compact\n * bracketed summary (chart variant, table dimensions) rather than raw row data — the rendered\n * artifact is the canonical view, and dumping CSV-shaped rows into a clipboard paste is noise.\n * Output is plain text with blank-line separators so it pastes cleanly into non-markdown targets.\n */\nexport function deckArtifactToClipboardText(artifact: DeckArtifactRecord): string {\n const header = artifact.title?.trim() || \"Deck\";\n const slides = artifact.config?.slides ?? [];\n const body = slides.map((slide, i) => slideToText(slide, i)).join(\"\\n\\n\");\n return body ? `${header}\\n\\n${body}` : header;\n}\n\n/** Renders a single slide as plain text for the clipboard. */\nfunction slideToText(slide: DeckSlide, index: number): string {\n const kind = slide.kind ?? deckSlideKinds.CONTENT;\n const prefix = `Slide ${index + 1}: `;\n switch (kind) {\n case deckSlideKinds.TITLE: {\n const parts = [slide.title];\n if (slide.subtitle) parts.push(slide.subtitle);\n if (slide.description) parts.push(slide.description);\n return `${prefix}${parts.join(\" — \")}`.trim();\n }\n case deckSlideKinds.AGENDA: {\n const items = (slide.items ?? []).map((item, i) => ` ${String(i + 1).padStart(2, \"0\")}. ${item}`).join(\"\\n\");\n return `${prefix}${slide.title || \"Agenda\"}${items ? `\\n${items}` : \"\"}`.trim();\n }\n case deckSlideKinds.SECTION:\n return `${prefix}${slide.title}`.trim();\n case deckSlideKinds.CLOSING:\n return `${prefix}Thank you`.trim();\n case deckSlideKinds.CONTENT:\n default: {\n // `?? []` guard: unlike the renderer, this can run on a RAW streaming payload (not yet\n // through `isValidDeckConfig`), where the agent may not have emitted `content` yet — without\n // the guard a content-less slide throws `Cannot read properties of undefined (reading 'map')`.\n const blocks = (slide.content ?? []).map(blockToText).filter(Boolean).join(\"\\n\\n\");\n return `${prefix}${slide.title}${blocks ? `\\n${blocks}` : \"\"}`.trim();\n }\n }\n}\n\n/** Renders a single content block as plain text for the clipboard. */\nfunction blockToText(block: DeckBlock): string {\n switch (block.type) {\n case deckBlockTypes.TEXT:\n return block.markdown.trim();\n case deckBlockTypes.CHART:\n return `[Chart: ${block.config.variant}]`;\n case deckBlockTypes.TABLE: {\n const columnCount = block.config.columns.length;\n const rowCount = block.rows.length;\n return `[Table: ${columnCount} column${columnCount === 1 ? \"\" : \"s\"} × ${rowCount} row${\n rowCount === 1 ? \"\" : \"s\"\n }]`;\n }\n default:\n return \"\";\n }\n}\n","import type { ArtifactService, DeckArtifactRecord } from \"../types\";\nimport { artifactTypes } from \"../types\";\nimport { DeckArtifact } from \"../deck-artifact/deck-artifact\";\nimport { deckArtifactToClipboardText } from \"../deck-artifact/to-clipboard-text\";\n\nexport const DeckArtifactService: ArtifactService<DeckArtifactRecord> = {\n type: artifactTypes.DECK,\n Component: DeckArtifact,\n toClipboardText: deckArtifactToClipboardText,\n};\n","import type { TableArtifactRecord } from \"@/components/artifacts/types\";\n\n/**\n * Produces a plain-text clipboard representation of a table artifact.\n *\n * Returns the artifact title as plain text so that pasting into non-markdown-aware targets\n * (plain-text editors, terminal, spreadsheets) does not expose raw markdown syntax. This\n * matches the contract of `TextBlockService.toClipboardText`, which also strips markdown —\n * `useCopyToClipboard` writes the aggregated string verbatim, so every block type must\n * independently normalise to plain text.\n *\n * The presigned URL is intentionally omitted from the clipboard value: presigned URLs expire\n * quickly (typically 15 min–1 h) and are not useful when pasted as bare text. The Download\n * button in the artifact chrome is the canonical way to obtain the file.\n */\nexport function tableArtifactToClipboardText(artifact: TableArtifactRecord): string {\n const trimmedTitle = artifact.title?.trim();\n return trimmedTitle || \"Table\";\n}\n","import type { ArtifactService, TableArtifactRecord } from \"../types\";\nimport { artifactTypes } from \"../types\";\nimport { TableArtifact } from \"../table-artifact/table-artifact\";\nimport { tableArtifactToClipboardText } from \"../table-artifact/to-clipboard-text\";\n\nexport const TableArtifactService: ArtifactService<TableArtifactRecord> = {\n type: artifactTypes.TABLE,\n Component: TableArtifact,\n toClipboardText: tableArtifactToClipboardText,\n};\n","import type { ArtifactRecord, ArtifactService, ArtifactType } from \"../types\";\nimport { artifactTypes } from \"../types\";\nimport { ChartArtifactService } from \"./chart-artifact-service\";\nimport { DeckArtifactService } from \"./deck-artifact-service\";\nimport { TableArtifactService } from \"./table-artifact-service\";\n\n// `ArtifactService<TSpecific>` is invariant on `TSpecific` via React's `ComponentType`\n// (props are contravariant). Each service overlaps structurally with the base `ArtifactService`\n// enough for a direct `as ArtifactService` cast. The registry dispatches only by runtime\n// discriminator — `ArtifactServices[artifact.type]` always resolves to the matching concrete service.\n/**\n * Map of artifact services keyed by `artifact.type`.\n *\n * Adding a new artifact type = create a `*-service.ts` file (mirroring `block-services/`), then\n * add one entry here. The block-level dispatcher (`ArtifactRefBlock`) consumes this map to\n * render the concrete component; `artifactToClipboardText` consumes it for the clipboard contract.\n */\nexport const ArtifactServices: Record<ArtifactType, ArtifactService> = {\n [artifactTypes.TABLE]: TableArtifactService as ArtifactService,\n [artifactTypes.CHART]: ChartArtifactService as ArtifactService,\n [artifactTypes.DECK]: DeckArtifactService as ArtifactService,\n};\n\n/**\n * Returns a clipboard-friendly string for an artifact, delegating to the registered service's\n * `toClipboardText`. Falls back to `[Artifact: <title>]` when the artifact type has no registered\n * service (unknown type) so copy never silently produces an empty string.\n */\nexport function artifactToClipboardText(artifact: ArtifactRecord): string {\n const entry = ArtifactServices[artifact.type];\n if (!entry) {\n return `[Artifact: ${artifact.title ?? artifact.type}]`;\n }\n return entry.toClipboardText(artifact);\n}\n"],"mappings":"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","names":["__iconNode","__iconNode","__iconNode","__iconNode","__iconNode","__iconNode","__iconNode","__iconNode","__iconNode","__iconNode","__iconNode","__iconNode","useCallback","jsx","useCallback","jsx","jsxs","DEFAULT_LABEL","useCallback","React","jsx","jsx","jsx","jsxs","useCallback","useEffect","useMemo","useRef","forwardRef","jsx","forwardRef","createElement","jsx","jsx","jsx","jsx","useEffect","useState","useState","useEffect","jsx","jsxs","useRef","useEffect","useMemo","useEffect","useMemo","useState","useCallback","useLayoutEffect","useRef","useState","jsx","jsxs","jsx","useRef","useState","jsx","jsxs","useRef","useState","jsx","jsxs","useState","useCallback","useRef","useLayoutEffect","jsx","jsxs","TITLE_CLASSES","useState","useEffect","useMemo","useEffect","useMemo","jsx","jsxs","TITLE_CLASSES","useEffect","useMemo"]}
@@ -83,10 +83,12 @@ var colors = {
83
83
  "red-200": "#FECACA",
84
84
  "red-400": "#F7776C",
85
85
  "red-500": "#EF4444",
86
- "red-600": "#DC2626"
86
+ "red-600": "#DC2626",
87
+ // Deck
88
+ "deck-canvas": "#faf8f5"
87
89
  };
88
90
 
89
91
  export {
90
92
  colors
91
93
  };
92
- //# sourceMappingURL=chunk-JF3P66JF.js.map
94
+ //# sourceMappingURL=chunk-SN3ORQSJ.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/tokens/colors.ts"],"sourcesContent":["export const colors = {\n // White\n white: \"#FFFFFF\",\n \"white-alpha-85\": \"rgba(255, 255, 255, 0.85)\",\n \"white-alpha-40\": \"rgba(255, 255, 255, 0.40)\",\n\n // Brown\n \"brown-10\": \"#FAF9F8\",\n \"brown-20\": \"#F5F3F0\",\n \"brown-30\": \"#EEEBE5\",\n \"brown-40\": \"#E6E2DA\",\n \"brown-50\": \"#CFCBC4\",\n \"brown-60\": \"#A19E99\",\n \"brown-70\": \"#73716D\",\n \"brown-80\": \"#5C5A57\",\n \"brown-90\": \"#2E2D2C\",\n \"brown-100\": \"#171716\",\n \"brown-alpha-12\": \"rgba(23, 23, 22, 0.12)\",\n \"brown-alpha-20\": \"rgba(23, 23, 22, 0.20)\",\n \"brown-alpha-55\": \"rgba(23, 23, 22, 0.55)\",\n \"brown-alpha-70\": \"rgba(23, 23, 22, 0.70)\",\n \"brown-alpha-80\": \"rgba(23, 23, 22, 0.80)\",\n \"brown-alpha-90\": \"rgba(23, 23, 22, 0.90)\",\n\n // Fuchsia\n \"fuchsia-50\": \"#FAF5FF\",\n \"fuchsia-300\": \"#F0ABFC\",\n \"fuchsia-500\": \"#D946EF\",\n \"fuchsia-600\": \"#C026D3\",\n \"fuchsia-800\": \"#86198F\",\n\n // Pink\n \"pink-50\": \"#FDF2F8\",\n \"pink-400\": \"#F472B6\",\n \"pink-600\": \"#DB2777\",\n \"pink-800\": \"#9D174D\",\n\n // Violet\n \"violet-50\": \"#F5F3FF\",\n \"violet-400\": \"#A78BFA\",\n \"violet-600\": \"#7C3AED\",\n \"violet-800\": \"#5B21B6\",\n\n // Indigo\n \"indigo-50\": \"#EEF2FF\",\n \"indigo-400\": \"#818CF8\",\n \"indigo-500\": \"#6366F1\",\n \"indigo-600\": \"#4F46E5\",\n \"indigo-800\": \"#3730A3\",\n\n // Cyan\n \"cyan-50\": \"#E7FEFF\",\n \"cyan-300\": \"#67E8F9\",\n \"cyan-600\": \"#0891B2\",\n \"cyan-900\": \"#164E63\",\n\n // Teal\n \"teal-50\": \"#EBFDF9\",\n \"teal-300\": \"#5EEAD4\",\n \"teal-600\": \"#0D9488\",\n\n // Rose\n \"rose-400\": \"#FB7185\",\n\n // Purple\n \"purple-400\": \"#C084FC\",\n\n // Blue\n \"blue-50\": \"#EFF6FF\",\n \"blue-100\": \"#DBEAFE\",\n \"blue-300\": \"#93C5FD\",\n \"blue-400\": \"#60A5FA\",\n \"blue-600\": \"#2563EB\",\n \"blue-800\": \"#1E40AF\",\n\n // Sky\n \"sky-300\": \"#7DD3FC\",\n\n // Green\n \"green-50\": \"#ECF9F0\",\n \"green-100\": \"#D4F1D9\",\n \"green-400\": \"#3DC269\",\n \"green-700\": \"#277C43\",\n \"green-800\": \"#1C5930\",\n\n // Orange\n \"orange-50\": \"#FFF7ED\",\n \"orange-100\": \"#FFEDD5\",\n \"orange-200\": \"#FED7AA\",\n \"orange-500\": \"#F97316\",\n \"orange-600\": \"#EA580C\",\n\n // Red\n \"red-50\": \"#FEF2F2\",\n \"red-100\": \"#FEE2E2\",\n \"red-200\": \"#FECACA\",\n \"red-400\": \"#F7776C\",\n \"red-500\": \"#EF4444\",\n \"red-600\": \"#DC2626\",\n} as const;\n\nexport type ColorToken = keyof typeof colors;\n"],"mappings":";AAAO,IAAM,SAAS;AAAA;AAAA,EAEpB,OAAO;AAAA,EACP,kBAAkB;AAAA,EAClB,kBAAkB;AAAA;AAAA,EAGlB,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,aAAa;AAAA,EACb,kBAAkB;AAAA,EAClB,kBAAkB;AAAA,EAClB,kBAAkB;AAAA,EAClB,kBAAkB;AAAA,EAClB,kBAAkB;AAAA,EAClB,kBAAkB;AAAA;AAAA,EAGlB,cAAc;AAAA,EACd,eAAe;AAAA,EACf,eAAe;AAAA,EACf,eAAe;AAAA,EACf,eAAe;AAAA;AAAA,EAGf,WAAW;AAAA,EACX,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,YAAY;AAAA;AAAA,EAGZ,aAAa;AAAA,EACb,cAAc;AAAA,EACd,cAAc;AAAA,EACd,cAAc;AAAA;AAAA,EAGd,aAAa;AAAA,EACb,cAAc;AAAA,EACd,cAAc;AAAA,EACd,cAAc;AAAA,EACd,cAAc;AAAA;AAAA,EAGd,WAAW;AAAA,EACX,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,YAAY;AAAA;AAAA,EAGZ,WAAW;AAAA,EACX,YAAY;AAAA,EACZ,YAAY;AAAA;AAAA,EAGZ,YAAY;AAAA;AAAA,EAGZ,cAAc;AAAA;AAAA,EAGd,WAAW;AAAA,EACX,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,YAAY;AAAA;AAAA,EAGZ,WAAW;AAAA;AAAA,EAGX,YAAY;AAAA,EACZ,aAAa;AAAA,EACb,aAAa;AAAA,EACb,aAAa;AAAA,EACb,aAAa;AAAA;AAAA,EAGb,aAAa;AAAA,EACb,cAAc;AAAA,EACd,cAAc;AAAA,EACd,cAAc;AAAA,EACd,cAAc;AAAA;AAAA,EAGd,UAAU;AAAA,EACV,WAAW;AAAA,EACX,WAAW;AAAA,EACX,WAAW;AAAA,EACX,WAAW;AAAA,EACX,WAAW;AACb;","names":[]}
1
+ {"version":3,"sources":["../src/tokens/colors.ts"],"sourcesContent":["export const colors = {\n // White\n white: \"#FFFFFF\",\n \"white-alpha-85\": \"rgba(255, 255, 255, 0.85)\",\n \"white-alpha-40\": \"rgba(255, 255, 255, 0.40)\",\n\n // Brown\n \"brown-10\": \"#FAF9F8\",\n \"brown-20\": \"#F5F3F0\",\n \"brown-30\": \"#EEEBE5\",\n \"brown-40\": \"#E6E2DA\",\n \"brown-50\": \"#CFCBC4\",\n \"brown-60\": \"#A19E99\",\n \"brown-70\": \"#73716D\",\n \"brown-80\": \"#5C5A57\",\n \"brown-90\": \"#2E2D2C\",\n \"brown-100\": \"#171716\",\n \"brown-alpha-12\": \"rgba(23, 23, 22, 0.12)\",\n \"brown-alpha-20\": \"rgba(23, 23, 22, 0.20)\",\n \"brown-alpha-55\": \"rgba(23, 23, 22, 0.55)\",\n \"brown-alpha-70\": \"rgba(23, 23, 22, 0.70)\",\n \"brown-alpha-80\": \"rgba(23, 23, 22, 0.80)\",\n \"brown-alpha-90\": \"rgba(23, 23, 22, 0.90)\",\n\n // Fuchsia\n \"fuchsia-50\": \"#FAF5FF\",\n \"fuchsia-300\": \"#F0ABFC\",\n \"fuchsia-500\": \"#D946EF\",\n \"fuchsia-600\": \"#C026D3\",\n \"fuchsia-800\": \"#86198F\",\n\n // Pink\n \"pink-50\": \"#FDF2F8\",\n \"pink-400\": \"#F472B6\",\n \"pink-600\": \"#DB2777\",\n \"pink-800\": \"#9D174D\",\n\n // Violet\n \"violet-50\": \"#F5F3FF\",\n \"violet-400\": \"#A78BFA\",\n \"violet-600\": \"#7C3AED\",\n \"violet-800\": \"#5B21B6\",\n\n // Indigo\n \"indigo-50\": \"#EEF2FF\",\n \"indigo-400\": \"#818CF8\",\n \"indigo-500\": \"#6366F1\",\n \"indigo-600\": \"#4F46E5\",\n \"indigo-800\": \"#3730A3\",\n\n // Cyan\n \"cyan-50\": \"#E7FEFF\",\n \"cyan-300\": \"#67E8F9\",\n \"cyan-600\": \"#0891B2\",\n \"cyan-900\": \"#164E63\",\n\n // Teal\n \"teal-50\": \"#EBFDF9\",\n \"teal-300\": \"#5EEAD4\",\n \"teal-600\": \"#0D9488\",\n\n // Rose\n \"rose-400\": \"#FB7185\",\n\n // Purple\n \"purple-400\": \"#C084FC\",\n\n // Blue\n \"blue-50\": \"#EFF6FF\",\n \"blue-100\": \"#DBEAFE\",\n \"blue-300\": \"#93C5FD\",\n \"blue-400\": \"#60A5FA\",\n \"blue-600\": \"#2563EB\",\n \"blue-800\": \"#1E40AF\",\n\n // Sky\n \"sky-300\": \"#7DD3FC\",\n\n // Green\n \"green-50\": \"#ECF9F0\",\n \"green-100\": \"#D4F1D9\",\n \"green-400\": \"#3DC269\",\n \"green-700\": \"#277C43\",\n \"green-800\": \"#1C5930\",\n\n // Orange\n \"orange-50\": \"#FFF7ED\",\n \"orange-100\": \"#FFEDD5\",\n \"orange-200\": \"#FED7AA\",\n \"orange-500\": \"#F97316\",\n \"orange-600\": \"#EA580C\",\n\n // Red\n \"red-50\": \"#FEF2F2\",\n \"red-100\": \"#FEE2E2\",\n \"red-200\": \"#FECACA\",\n \"red-400\": \"#F7776C\",\n \"red-500\": \"#EF4444\",\n \"red-600\": \"#DC2626\",\n\n // Deck\n \"deck-canvas\": \"#faf8f5\",\n} as const;\n\nexport type ColorToken = keyof typeof colors;\n"],"mappings":";AAAO,IAAM,SAAS;AAAA;AAAA,EAEpB,OAAO;AAAA,EACP,kBAAkB;AAAA,EAClB,kBAAkB;AAAA;AAAA,EAGlB,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,aAAa;AAAA,EACb,kBAAkB;AAAA,EAClB,kBAAkB;AAAA,EAClB,kBAAkB;AAAA,EAClB,kBAAkB;AAAA,EAClB,kBAAkB;AAAA,EAClB,kBAAkB;AAAA;AAAA,EAGlB,cAAc;AAAA,EACd,eAAe;AAAA,EACf,eAAe;AAAA,EACf,eAAe;AAAA,EACf,eAAe;AAAA;AAAA,EAGf,WAAW;AAAA,EACX,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,YAAY;AAAA;AAAA,EAGZ,aAAa;AAAA,EACb,cAAc;AAAA,EACd,cAAc;AAAA,EACd,cAAc;AAAA;AAAA,EAGd,aAAa;AAAA,EACb,cAAc;AAAA,EACd,cAAc;AAAA,EACd,cAAc;AAAA,EACd,cAAc;AAAA;AAAA,EAGd,WAAW;AAAA,EACX,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,YAAY;AAAA;AAAA,EAGZ,WAAW;AAAA,EACX,YAAY;AAAA,EACZ,YAAY;AAAA;AAAA,EAGZ,YAAY;AAAA;AAAA,EAGZ,cAAc;AAAA;AAAA,EAGd,WAAW;AAAA,EACX,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,YAAY;AAAA;AAAA,EAGZ,WAAW;AAAA;AAAA,EAGX,YAAY;AAAA,EACZ,aAAa;AAAA,EACb,aAAa;AAAA,EACb,aAAa;AAAA,EACb,aAAa;AAAA;AAAA,EAGb,aAAa;AAAA,EACb,cAAc;AAAA,EACd,cAAc;AAAA,EACd,cAAc;AAAA,EACd,cAAc;AAAA;AAAA,EAGd,UAAU;AAAA,EACV,WAAW;AAAA,EACX,WAAW;AAAA,EACX,WAAW;AAAA,EACX,WAAW;AAAA,EACX,WAAW;AAAA;AAAA,EAGX,eAAe;AACjB;","names":[]}