@shapesos/clay 0.13.0 → 0.13.1

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.
package/dist/chat.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/capture/capture-exclude-attribute.ts","../src/tokens/colors.ts","../src/capture/_font-data-urls.generated.ts","../src/capture/font-embed.ts","../src/capture/dom-to-image.ts","../src/components/chat/index.ts","../src/components/chat/chat-message/chat-message.tsx","../src/components/blocks/types.ts","../src/components/artifacts/artifact-base.ts","../src/components/artifacts/chart-artifact/types.ts","../src/components/artifacts/table-artifact/types.ts","../src/components/artifacts/chart-artifact/chart-artifact.tsx","../src/components/ai-elements/artifact.tsx","../src/lib/utils.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/IconCheck.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/icon-button/icon-button.tsx","../src/components/icon/icon.tsx","../src/components/icon/icon-styles.ts","../src/components/icon-button/icon-button-styles.ts","../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/status-message.tsx","../src/components/chart/bar-chart/bar-chart.tsx","../src/components/chart/chart-container.tsx","../src/components/chart/chart-legend-content.tsx","../src/components/chart/chart-tooltip-content.tsx","../src/tokens/index.ts","../src/tokens/typography.ts","../src/components/chart/chart-palette.ts","../src/components/chart/chart-styles.ts","../src/components/chart/chart-tick-utils.ts","../src/components/chart/coerce-numeric.ts","../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/chart/line-chart/line-chart.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/chart/pie-chart/pie-chart.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/chart-artifact/to-clipboard-text.ts","../src/components/artifacts/artifact-services/chart-artifact-service.ts","../src/components/artifacts/table-artifact/table-artifact.tsx","../src/components/ui/table.tsx","../src/components/table/use-scroll-shadow.ts","../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/table-artifact/use-table-artifact-data.ts","../src/components/artifacts/table-artifact/validate.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","../src/components/blocks/artifact-ref-block/artifact-ref-block.tsx","../src/components/blocks/block-services/artifact-ref-block-service.ts","../src/utils/markdown-to-plain-text.ts","../src/components/blocks/text-block/text-block.tsx","../src/components/blocks/scrollable-table/scrollable-table-styles.ts","../src/components/blocks/scrollable-table/scrollable-table.tsx","../src/components/blocks/text-block/sanitize.ts","../src/components/blocks/text-block/text-block-styles.ts","../src/components/blocks/block-services/text-block-service.ts","../src/components/blocks/block-services/index.ts","../src/components/blocks/block/block.tsx","../src/components/chat/hooks/copy-message-text.ts","../src/components/chat/chat-message-actions/chat-message-actions.tsx","../src/components/chat/chat-context/chat-context.ts","../src/components/chat/hooks/use-copy-to-clipboard.ts","../src/utils/clipboard.ts","../src/components/chat/constants.ts","../src/components/chat/chat-message-actions/chat-message-actions-styles.ts","../src/components/chat/chat-message/chat-message-styles.ts"],"sourcesContent":["/**\n * Marker attribute the default filter uses to exclude subtrees from a DOM-to-image capture.\n * Apply it to elements that should not appear in the rendered image — e.g. action toolbars\n * that would otherwise capture themselves.\n *\n * **Important**: do not apply this attribute to the capture root itself. The default filter\n * runs on every descendant *including* the root; marking the root excludes the entire subtree\n * and `toBlob()` returns `null`, surfacing as a \"Copy failed\" state in the UI. Consumers should\n * apply it to a child (typically the actions toolbar), never to the same node referenced by\n * `targetRef`.\n *\n * Lives in its own module so consumers like `chart-artifact.tsx` can import the string\n * constant without dragging in the heavy `dom-to-image.ts` module (font data URLs +\n * html-to-image). Re-exported from both `dom-to-image.ts` (for the default filter to read)\n * and `capture/index.ts` (for external consumers).\n */\nexport const CAPTURE_EXCLUDE_ATTRIBUTE = \"data-clay-capture-exclude\";\n","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","/**\n * AUTO-GENERATED — DO NOT EDIT BY HAND.\n *\n * Run `bun run gen:font` (also runs automatically before `bun run build`) to refresh from the\n * `@fontsource-variable/*` packages' woff2 binaries. See `scripts/generate-font-embed.ts` for\n * why this exists as a pre-build step rather than a tsup loader.\n */\n\nexport const GEIST_LATIN_WOFF2_DATA_URL: string =\n \"data:font/woff2;base64,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\";\n\nexport const CRIMSON_PRO_LATIN_WOFF2_DATA_URL: string =\n \"data:font/woff2;base64,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\";\n","// Data URLs are generated at build time by `scripts/generate-font-embed.ts` (auto-run via\n// `bun run gen:font` before every `bun run build`). Pre-computing as a `.ts` file sidesteps\n// tsup's `.woff2` loader entirely — esbuild's `noExternal` re-bundling does not apply asset\n// loaders to imports inside re-bundled deps, which leaves bare `.woff2` imports in the output\n// and crashes downstream bundlers. See `scripts/generate-font-embed.ts` for the full story.\nimport { CRIMSON_PRO_LATIN_WOFF2_DATA_URL, GEIST_LATIN_WOFF2_DATA_URL } from \"./_font-data-urls.generated\";\n\n/**\n * `@font-face` CSS for clay's two type-system fonts — Geist (primary sans) and Crimson Pro\n * (secondary serif). Both binaries are bundled as base64 data URLs — no runtime fetch required.\n *\n * Why bundle both: `html-to-image` rasterises through an SVG `<foreignObject>`, which renders\n * in a separate document context from the page. Fonts loaded in the page (e.g. via a Google\n * Fonts `<link>`) are NOT visible to the foreignObject — only fonts whose `@font-face` rule is\n * inlined in the captured CSS are. `getFontEmbedCSS` tries to inline them by reading\n * `document.styleSheets`, but cross-origin stylesheets (Google Fonts, any CDN) throw\n * `SecurityError` on `cssRules` access. The fallback fetch is then blocked by CORS preflight.\n * So we ship both with clay and skip the runtime-resolution mess entirely. Any chart artifact\n * that uses either family captures correctly; consumers don't need to mirror the font setup.\n *\n * `format(\"woff2-variations\")` is the correct format hint for a variable axis font — browsers\n * supporting variable fonts pick this up; older browsers ignore the rule and fall back to the\n * next entry in the font stack.\n *\n * Subset is `latin` only for both (~29 KB Geist + ~47 KB Crimson Pro on disk → ~40 KB + ~63 KB\n * base64). Latin covers all chat-UI English content; consumers that need Hebrew / Cyrillic /\n * CJK PNG capture pass their own `fontEmbedCSS` via `NodeToImageOptions.fontEmbedCSS`.\n */\nexport const CLAY_FONTS_EMBED_CSS = `\n@font-face {\n font-family: 'Geist';\n font-style: normal;\n font-weight: 100 900;\n font-display: swap;\n src: url(${GEIST_LATIN_WOFF2_DATA_URL}) format('woff2-variations');\n}\n@font-face {\n font-family: 'Crimson Pro';\n font-style: normal;\n font-weight: 200 900;\n font-display: swap;\n src: url(${CRIMSON_PRO_LATIN_WOFF2_DATA_URL}) format('woff2-variations');\n}\n`.trim();\n","import { getFontEmbedCSS, toBlob } from \"html-to-image\";\n\nimport { colors } from \"@/tokens/colors\";\n\nimport { CAPTURE_EXCLUDE_ATTRIBUTE } from \"./capture-exclude-attribute\";\nimport { CLAY_FONTS_EMBED_CSS } from \"./font-embed\";\n\n// Re-export so external consumers of `@shapesos/clay/capture` keep working unchanged. The\n// canonical definition lives in `./capture-exclude-attribute` so consumers like\n// `chart-artifact.tsx` can import the string without pulling in this heavy module.\nexport { CAPTURE_EXCLUDE_ATTRIBUTE };\n\n/** Options for DOM-to-image conversions. Forwarded to `html-to-image`'s `toBlob`. */\nexport interface NodeToImageOptions {\n /**\n * Background color filled before rasterising. Default: clay `white` token (`#FFFFFF`).\n * A solid background prevents the PNG from looking broken when pasted onto light or dark\n * surfaces (Slack, Notion, VS Code, etc.) — transparent PNGs render unpredictably.\n */\n backgroundColor?: string;\n /**\n * Device pixel ratio for retina-sharp output. Default: `window.devicePixelRatio || 1`.\n *\n * Memory implication: each copy allocates a canvas backing-store sized to\n * `width × height × pixelRatio²`. On a 3× HiDPI display a 480px-wide chart card produces a\n * ~1440px PNG (~8 MB allocation per copy). Single-click use is fine; batch-capture flows\n * (\"copy every chart in this report\") should pass `pixelRatio: 1` to avoid stacking spikes.\n */\n pixelRatio?: number;\n /**\n * Predicate run against every descendant element. Returning `false` excludes the node and\n * its subtree from the image. The clay default excludes any element marked with\n * `data-clay-capture-exclude` — caller-supplied filters compose with (not replace) the\n * default: a node is included only when both the default and the caller filter accept it.\n */\n filter?: (node: HTMLElement) => boolean;\n /** Override the captured width in pixels. Default: `node.offsetWidth`. */\n width?: number;\n /** Override the captured height in pixels. Default: `node.offsetHeight`. */\n height?: number;\n /**\n * Force-reload `<img>` sources to defeat stale-cache rendering. Default: `true`.\n * Note that this only cache-busts `<img>` `src` resources — fonts and CSS-referenced URLs\n * (e.g. `background-image: url(...)`) go through `html-to-image`'s separate global URL cache\n * which does NOT honor this flag.\n */\n cacheBust?: boolean;\n /**\n * Pre-resolved `@font-face` CSS so embedded text uses the correct fonts. When omitted, this\n * module resolves the document's font-embed CSS lazily on the first capture and reuses the\n * cached value thereafter — avoiding a recurring main-thread block to re-walk\n * `document.styleSheets` and re-base64-encode webfont files on every copy.\n */\n fontEmbedCSS?: string;\n}\n\nconst DEFAULT_BACKGROUND = colors.white;\n\n/** Truncation cap for error messages surfaced in UI tooltips. Long S3 presigned URLs or\n * stringified stack frames from `html-to-image`'s internals can otherwise leak verbatim. */\nconst MAX_ERROR_MESSAGE_CHARS = 120;\n\nfunction defaultFilter(node: HTMLElement): boolean {\n if (typeof node.getAttribute !== \"function\") return true;\n return !node.hasAttribute(CAPTURE_EXCLUDE_ATTRIBUTE);\n}\n\nfunction composeFilter(caller: NodeToImageOptions[\"filter\"]): (node: HTMLElement) => boolean {\n if (!caller) return defaultFilter;\n return (node) => defaultFilter(node) && caller(node);\n}\n\n/**\n * Lazy singleton for the document's resolved `@font-face` embed CSS.\n *\n * The result is always **prefixed with clay's bundled Geist CSS** (see `font-embed.ts`), then\n * appended with whatever `html-to-image`'s `getFontEmbedCSS` can extract from\n * `document.styleSheets`. The bundled prefix guarantees Geist renders in the captured PNG\n * even when the consumer page loads Geist from a CORS-walled source (Google Fonts is the\n * common case — its CSS is unreadable from JS so `getFontEmbedCSS` returns an empty string\n * and the foreignObject falls back to a system font, which renders wider than Geist and makes\n * the captured layout reflow at apparent narrower widths). Anything else\n * `html-to-image` discovers (consumer's own webfonts, icon fonts, etc.) is layered on top.\n *\n * Computed once per session and cached — avoids repeated `document.styleSheets` traversals\n * and webfont URL fetches that would each add 100–500 ms of main-thread work per copy.\n */\nlet cachedFontEmbedCSS: string | null = null;\nlet inflightFontEmbedCSS: Promise<string> | null = null;\n\nasync function resolveFontEmbedCSS(node: HTMLElement): Promise<string> {\n if (cachedFontEmbedCSS !== null) return cachedFontEmbedCSS;\n if (inflightFontEmbedCSS !== null) return inflightFontEmbedCSS;\n inflightFontEmbedCSS = getFontEmbedCSS(node)\n .catch(() => \"\")\n .then((dynamicCss) => {\n const combined = `${CLAY_FONTS_EMBED_CSS}\\n${dynamicCss}`;\n cachedFontEmbedCSS = combined;\n return combined;\n })\n .finally(() => {\n inflightFontEmbedCSS = null;\n });\n return inflightFontEmbedCSS;\n}\n\n/**\n * Test-only escape hatch to clear the module-level `cachedFontEmbedCSS` / `inflightFontEmbedCSS`\n * singletons. Vitest's default worker pool re-uses module instances across tests in the same\n * file; without an explicit reset, the first test populates the cache and every subsequent test\n * skips `getFontEmbedCSS` entirely — masking any per-test mocking the caller set up.\n *\n * Call in `beforeEach` of any test file that exercises `nodeToPngBlob` / `copyNodeAsPng` /\n * `downloadNodeAsPng` and depends on a fresh font-CSS resolution. Not exported from\n * `@shapesos/clay/capture` (the leading underscore marks it as internal).\n */\nexport function _resetFontEmbedCacheForTests(): void {\n cachedFontEmbedCSS = null;\n inflightFontEmbedCSS = null;\n}\n\n/**\n * Rasterise `node` to a PNG `Blob`. **Browser-only at call time**, but **import-safe under SSR**:\n * the module itself does not touch browser globals at module load, so a Node / SSR consumer can\n * `import { nodeToPngBlob } from \"@shapesos/clay/capture\"` without crashing. Calling any of the\n * three capture functions in a non-browser context throws — `nodeToPngBlob` references `<canvas>`\n * via `html-to-image`; `copyNodeAsPng` checks `ClipboardItem` / `navigator.clipboard` explicitly;\n * `downloadNodeAsPng` uses `URL.createObjectURL` + `document`. Guard at the call site with\n * `typeof window !== \"undefined\"` if you need to short-circuit SSR.\n *\n * Throws when `html-to-image` reports a null blob (browser couldn't rasterise) or when the\n * underlying conversion rejects.\n *\n * **Caveats** (worth knowing before adopting in a new artifact type):\n * - **Cross-origin tainting**: if the captured subtree contains a cross-origin `<img>`,\n * `<image href>` SVG element, or any other resource the browser flags as cross-origin, the\n * canvas is tainted and `toBlob()` returns `null` — surfaced as a \"Failed to rasterise\" error.\n * This is browser-enforced; no clay-side fix exists. Ensure user-controlled annotations in\n * chart subtrees don't reference cross-origin resources.\n * - **Global URL cache in the underlying library**: `html-to-image` maintains a module-global\n * cache of image-src → base64. The cache is per-tab and lives for the SPA session. Today\n * clay's only consumer (chart artifacts) renders SVG, so the cache stays empty in practice —\n * but future artifact types that embed user-uploaded `<img>` content should be aware that\n * image URLs are cached by URL, not by user identity.\n */\nexport async function nodeToPngBlob(node: HTMLElement, options: NodeToImageOptions = {}): Promise<Blob> {\n const fontEmbedCSS = options.fontEmbedCSS ?? (await resolveFontEmbedCSS(node).catch(() => undefined));\n // Default width/height from the node's actual rendered dimensions. Without explicit values,\n // `html-to-image` uses `node.offsetWidth` for the *canvas* size but does NOT pin the cloned\n // root's CSS width — so when the clone is placed offscreen for serialisation, `w-full` and\n // other percentage-width descendants re-evaluate against whatever the offscreen container's\n // effective width is, causing HTML elements (titles, legends, anything not yet a rasterised\n // SVG attribute) to reflow at a narrower width than the live element. Passing explicit\n // width/height applies them as inline styles on the cloned root, locking the layout context.\n // `getBoundingClientRect()` is subpixel-accurate where `offsetWidth` rounds to integers —\n // matters for layouts that wrap at sub-pixel boundaries (Tailwind's `min-w-[…]` constraints\n // commonly land here).\n const rect = node.getBoundingClientRect();\n const width = options.width ?? rect.width;\n const height = options.height ?? rect.height;\n const blob = await toBlob(node, {\n backgroundColor: options.backgroundColor ?? DEFAULT_BACKGROUND,\n cacheBust: options.cacheBust ?? true,\n filter: composeFilter(options.filter),\n pixelRatio: options.pixelRatio,\n width,\n height,\n fontEmbedCSS,\n });\n if (!blob) throw new Error(\"Failed to rasterise node — toBlob returned null (possible cross-origin canvas taint).\");\n return blob;\n}\n\n/**\n * Rasterise `node` and write the resulting PNG to the system clipboard.\n *\n * Must be called from within a user gesture (button click, keypress) — browsers reject\n * clipboard writes from non-gesture contexts. The in-flight `Promise<Blob>` is passed\n * directly into `ClipboardItem` rather than awaited first, so Safari's \"Blob must be\n * created during the user gesture\" rule is satisfied.\n *\n * Throws when the clipboard API is unavailable (non-secure contexts: HTTP, certain embedded\n * frames), when the rasterise fails, or when the user has revoked clipboard-write permission.\n */\nexport async function copyNodeAsPng(node: HTMLElement, options: NodeToImageOptions = {}): Promise<void> {\n if (typeof ClipboardItem === \"undefined\") {\n throw new Error(\"Clipboard image writes are not supported in this environment (ClipboardItem missing).\");\n }\n if (!navigator.clipboard) {\n throw new Error(\"Clipboard API unavailable — secure context (HTTPS) required.\");\n }\n\n const blobPromise = nodeToPngBlob(node, options);\n // Attach a no-op catch so the rejection doesn't surface as \"unhandled\" before the clipboard\n // write rejects with the same error. The original promise is still awaited below.\n void blobPromise.catch(() => undefined);\n\n const item = new ClipboardItem({ \"image/png\": blobPromise });\n // Browsers reject `write()` if the promise inside the ClipboardItem rejects. Run the write\n // and the underlying blob fetch in parallel — whichever surfaces first is the error.\n await Promise.all([navigator.clipboard.write([item]), blobPromise]);\n}\n\n/**\n * Window between `<a>.click()` and `URL.revokeObjectURL` for the object URL used by\n * `downloadNodeAsPng`. The click triggers the download asynchronously — Firefox in\n * particular reads the blob URL on a separate task and will fail silently if the URL is\n * revoked before that read completes. 30s is well past any browser's start-the-download\n * latency budget and small enough that even abandoned downloads don't hold the blob in\n * memory for more than the user's typical interactive session.\n */\nconst OBJECT_URL_REVOKE_DELAY_MS = 30_000;\n\n/**\n * Rasterise `node` and trigger a browser file download of the resulting PNG. Uses an\n * `<a download>` with an object URL — the URL is revoked on a deferred timer so it doesn't\n * leak into the document for the lifetime of the tab but also isn't revoked before the\n * browser reads it.\n *\n * Must be called from a user gesture (the `<a>.click()` only triggers a download from inside one).\n *\n * @param filename The suggested filename presented to the user (e.g. `\"chart-2026-05-25.png\"`).\n * The `.png` extension is NOT appended automatically — callers control the full\n * filename so they can include titles, dates, or artifact IDs.\n */\nexport async function downloadNodeAsPng(\n node: HTMLElement,\n filename: string,\n options: NodeToImageOptions = {}\n): Promise<void> {\n const blob = await nodeToPngBlob(node, options);\n const url = URL.createObjectURL(blob);\n const a = document.createElement(\"a\");\n a.href = url;\n a.download = filename;\n a.rel = \"noopener noreferrer\";\n // Anchors must be in the DOM for some browsers (Firefox) to honour programmatic clicks\n // that trigger downloads. Append → click → remove keeps the side-effect surface minimal.\n document.body.appendChild(a);\n a.click();\n a.remove();\n // Defer the revoke: `a.click()` initiates the download asynchronously and Firefox reads the\n // blob URL on a separate task. A synchronous revoke (in a `finally` block immediately after\n // click) tears down the URL before the browser starts the read → silent download failure.\n // See Cursor Bugbot report on PR #60 + html-to-image issue tracker. The setTimeout is fire-and-\n // forget; if the tab closes first, the URL is freed by the document teardown anyway.\n setTimeout(() => URL.revokeObjectURL(url), OBJECT_URL_REVOKE_DELAY_MS);\n}\n\n/**\n * Trim an error message to a UI-safe length. Verbose error sources (`html-to-image` internals,\n * fetch failures with full S3 presigned URLs) can otherwise produce multi-kilobyte strings that\n * blow out tooltips and leak unnecessary detail.\n */\nexport function truncateErrorMessage(message: string, max = MAX_ERROR_MESSAGE_CHARS): string {\n return message.length <= max ? message : `${message.slice(0, max - 1)}…`;\n}\n","export { ChatMessage } from \"./chat-message/chat-message\";\nexport { useChatContext, ChatContext } from \"./chat-context/chat-context\";\nexport { useCopyToClipboard } from \"./hooks/use-copy-to-clipboard\";\nexport { copyMessageText } from \"./hooks/copy-message-text\";\nexport { MESSAGE_ROLE } from \"./constants\";\nexport type { ChatMessage as ChatMessageType, MessageRole, ChatContextValue } from \"./types\";\n","import { useMemo } from \"react\";\n\nimport { Block } from \"@/components/blocks/block\";\nimport { copyMessageText } from \"../hooks/copy-message-text\";\nimport type { ChatMessage as ChatMessageType } from \"../types\";\nimport { ChatMessageActions } from \"../chat-message-actions/chat-message-actions\";\nimport { MessageRow, MessageBubble, MessageContainer } from \"./chat-message-styles\";\n\ninterface ChatMessageProps {\n message: ChatMessageType;\n}\n\nexport function ChatMessage({ message }: ChatMessageProps) {\n // Aggregate per-block clipboard text so the copy button produces richer output than the\n // server-supplied `fallbackText` (which is typically \"[table: Title]\" for artifact blocks).\n // `useMemo` keeps the string stable across re-renders that don't change the blocks array.\n const clipboardText = useMemo(() => copyMessageText(message), [message]);\n\n return (\n <MessageRow $role={message.role} role=\"article\" aria-label={`${message.role} message`}>\n <MessageContainer $role={message.role}>\n <MessageBubble $role={message.role} data-testid=\"message-bubble\">\n {message.blocks.map((block, index) => (\n <Block key={index} block={block} />\n ))}\n </MessageBubble>\n <ChatMessageActions\n messageId={message.id}\n content={clipboardText}\n role={message.role}\n isHelpful={message.isHelpful}\n />\n </MessageContainer>\n </MessageRow>\n );\n}\n","import type { ComponentType } from \"react\";\n\nimport type { ArtifactCallbacksOf, ArtifactLabelsOf, ArtifactRecord } from \"@/components/artifacts/types\";\n\n/** Block-type discriminator constants. Mirrors the wire shape from io-server / shapes-agent. */\nexport const blockTypes = {\n TEXT: \"TEXT\",\n ARTIFACT_REF: \"ARTIFACT_REF\",\n} as const;\n\n/** Discriminator for content blocks. */\nexport type BlockType = (typeof blockTypes)[keyof typeof blockTypes];\n\n/** Plain-text content block — rendered as Markdown. */\nexport interface TextBlockRecord {\n /** Block-type discriminator. */\n type: typeof blockTypes.TEXT;\n /** Type-specific data; kept as `payload` to match the wire shape 1:1. */\n payload: {\n /** The Markdown source for this text segment. */\n text: string;\n };\n}\n\n/**\n * Reference-to-an-artifact content block — dispatched to the per-type artifact component.\n *\n * The artifact-domain types (`ArtifactRecord`, `TableArtifactRecord`, etc.) live in\n * `@/components/artifacts/types` so the artifact layer can be consumed independently of the\n * block layer (e.g. by a future surface that renders an artifact outside chat). The block layer\n * imports them here to wrap an artifact in an `ARTIFACT_REF` block.\n *\n * `ArtifactRefBlockRecord` is a *distributed* union over `ArtifactRecord` branches — TS narrows\n * `labels` and `callbacks` to the slice matching `artifact.type` via `ArtifactLabelsOf<T>` /\n * `ArtifactCallbacksOf<T>`. Without this distribution, `Partial<ChartLabels | TableLabels>`\n * collapses to `Partial<ChartLabels> | Partial<TableLabels>` and TS accepts chart-only keys on\n * a TABLE block's payload (the discriminator becomes load-bearing only at runtime, not in the\n * type system).\n */\nexport type ArtifactRefBlockRecord = ArtifactRecord extends infer TArtifact\n ? TArtifact extends ArtifactRecord\n ? {\n /** Block-type discriminator. */\n type: typeof blockTypes.ARTIFACT_REF;\n /** Type-specific data; kept as `payload` to match the wire shape 1:1. */\n payload: {\n /** The artifact's stable identifier, used for keying / future cross-message reuse. */\n artifactId: string;\n /** Inlined artifact metadata — clients fetch the underlying data via `artifact.protectedAsset.presignedUrl`. */\n artifact: TArtifact;\n /**\n * Optional consumer-supplied labels for the rendered artifact's action buttons + status\n * messages — a flat slice narrowed by `artifact.type`. NOT a wire field; the consumer\n * constructs this from their i18n layer when materialising the block-record from the\n * server payload, then passes the augmented block to `<Block>`. The dispatcher forwards\n * the slice directly to the matching renderer — no per-type key wrapping (the block\n * already carries the discriminator).\n *\n * TS catches cross-type assignments here: `block.payload.labels = { downloadCsv: 'X' }`\n * on a `TableArtifactRecord`-typed block is a type error because `downloadCsv` isn't a\n * `TableArtifactLabels` key.\n */\n labels?: Partial<ArtifactLabelsOf<TArtifact>>;\n /**\n * Optional side-effect callbacks for actions on the rendered artifact (e.g. `onDownload`).\n * Flat slice narrowed by `artifact.type` via `ArtifactCallbacksOf<T>`. NOT a wire field\n * — the consumer wires this once to bridge artifact actions to their analytics /\n * observability layer.\n */\n callbacks?: Partial<ArtifactCallbacksOf<TArtifact>>;\n };\n }\n : never\n : never;\n\n/** Discriminated union of all supported content blocks. Additive — new types extend this union. */\nexport type BlockRecord = TextBlockRecord | ArtifactRefBlockRecord;\n\n/** Props every concrete block component receives from the dispatcher. */\nexport interface BlockComponentProps<TBlock extends BlockRecord = BlockRecord> {\n /** The block to render. */\n block: TBlock;\n}\n\n/** Per-block-type service — registry entry that pairs a discriminator with its concrete component. */\nexport interface BlockService<TBlock extends BlockRecord = BlockRecord> {\n /** Discriminator value this service handles. */\n type: BlockType;\n /** React component that renders a block of this type. */\n Component: ComponentType<BlockComponentProps<TBlock>>;\n /**\n * Returns a plain-text or markdown string representation of this block suitable for clipboard\n * copy. Called by `copyMessageText` when the user clicks \"copy message\".\n *\n * - `TextBlockService`: returns `block.payload.text` verbatim (markdown preserved).\n * - `ArtifactRefBlockService`: delegates to `ArtifactServices[artifact.type].toClipboardText`.\n * Falls back to `[Artifact: <title>]` when the artifact type has no registered component.\n *\n * Optional so external `BlockService` implementors don't break on upgrade — when omitted, the\n * aggregator treats the block as contributing the empty string (silently skipped). Implementations\n * must be pure, synchronous functions — no async, no hooks, no side effects.\n */\n toClipboardText?: (block: TBlock) => string;\n}\n","/**\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} 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 { 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 { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\n/**\n * Combines class names using clsx and tailwind-merge — the standard shadcn/ui helper.\n * Tailwind classes that conflict (e.g. `p-2 p-4`) get merged so the last one wins.\n */\nexport function cn(...inputs: ClassValue[]): string {\n return twMerge(clsx(inputs));\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\":\"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\":\"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 { forwardRef } from \"react\";\n\nimport { Icon } from \"../icon/icon\";\nimport { Button } from \"./icon-button-styles\";\nimport type { IconButtonProps, IconButtonSize } from \"./types\";\n\nconst ICON_SIZE_BY_BUTTON_SIZE: Record<IconButtonSize, number> = {\n small: 16,\n medium: 20,\n};\n\n/** Ghost-style icon button. `forwardRef` + the rest-props spread so Radix `Slot`-based wrappers\n * (Tooltip, Popover, DropdownMenu) can attach refs and event handlers via `asChild`. */\nexport const IconButton = forwardRef<HTMLButtonElement, IconButtonProps>(function IconButton(\n {\n icon,\n size = \"small\",\n variant = \"short\",\n isSelected = false,\n disabled = false,\n onClick,\n className,\n id,\n as,\n \"aria-label\": ariaLabel,\n ...rest\n },\n ref\n) {\n // Only set `type=\"button\"` when rendering as a native button. Anchors (`as=\"a\"`) treat the\n // `type` attribute as a content-type hint for the linked resource — `type=\"button\"` on an\n // anchor is semantically wrong and shows up as an a11y / HTML-validation noise warning.\n const isNativeButton = as === undefined || as === \"button\";\n return (\n <Button\n ref={ref}\n className={className}\n id={id}\n as={as}\n $size={size}\n $variant={variant}\n $isSelected={isSelected}\n disabled={disabled}\n onClick={disabled ? undefined : onClick}\n aria-label={ariaLabel}\n {...(isNativeButton ? { type: \"button\" as const } : {})}\n {...rest}\n >\n <Icon icon={icon} size={ICON_SIZE_BY_BUTTON_SIZE[size]} />\n </Button>\n );\n});\n","import { forwardRef } from \"react\";\n\nimport { IconWrapper, getStrokeWidth } from \"./icon-styles\";\nimport type { IconProps } from \"./types\";\n\nconst DEFAULT_SIZE = 16;\n\nexport const Icon = forwardRef<HTMLSpanElement, IconProps>(function Icon(\n { icon: IconComponent, size = DEFAULT_SIZE, color, className, \"aria-label\": ariaLabel },\n ref\n) {\n return (\n <IconWrapper\n ref={ref}\n className={className}\n $color={color}\n aria-label={ariaLabel}\n role={ariaLabel ? \"img\" : undefined}\n >\n <IconComponent width={size} height={size} strokeWidth={getStrokeWidth(size)} />\n </IconWrapper>\n );\n});\n","import styled from \"styled-components\";\n\nexport const STROKE_WIDTH_BY_SIZE: Record<number, number> = {\n 12: 1.4,\n 14: 1.4,\n 16: 1.8,\n 18: 1.8,\n 20: 1.8,\n 24: 1.8,\n};\n\nexport const DEFAULT_STROKE_WIDTH = 1.8;\n\nexport function getStrokeWidth(size: number): number {\n return STROKE_WIDTH_BY_SIZE[size] ?? DEFAULT_STROKE_WIDTH;\n}\n\nexport const IconWrapper = styled.span<{ $color?: string }>`\n display: inline-flex;\n align-items: center;\n justify-content: center;\n color: ${({ $color }) => $color ?? \"currentColor\"};\n flex-shrink: 0;\n`;\n","import styled from \"styled-components\";\n\nimport { colors } from \"../../tokens/colors\";\nimport type { IconButtonSize, IconButtonVariant } from \"./types\";\n\nconst PADDING: Record<IconButtonVariant, Record<IconButtonSize, string>> = {\n short: {\n small: \"4px\",\n medium: \"6px\",\n },\n long: {\n small: \"4px 6px\",\n medium: \"6px 10px\",\n },\n};\n\nconst BORDER_RADIUS: Record<IconButtonSize, number> = {\n small: 6,\n medium: 10,\n};\n\nexport const Button = styled.button<{\n $size: IconButtonSize;\n $variant: IconButtonVariant;\n $isSelected: boolean;\n}>`\n display: inline-flex;\n align-items: center;\n justify-content: center;\n padding: ${({ $size, $variant }) => PADDING[$variant][$size]};\n border: none;\n border-radius: ${({ $size }) => BORDER_RADIUS[$size]}px;\n background: ${({ $isSelected }) => ($isSelected ? colors[\"brown-40\"] : \"transparent\")};\n color: ${colors[\"brown-100\"]};\n cursor: pointer;\n transition: background-color 100ms ease;\n\n &:hover {\n background: ${({ $isSelected }) => ($isSelected ? colors[\"brown-40\"] : colors[\"brown-20\"])};\n }\n\n &:active {\n background: ${colors[\"brown-40\"]};\n }\n\n &:disabled {\n color: ${colors[\"brown-50\"]};\n background: transparent;\n cursor: not-allowed;\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 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","import { type ReactNode, useMemo } from \"react\";\nimport { Bar, BarChart as RechartsBarChart, CartesianGrid, LabelList, Legend, Tooltip, XAxis, YAxis } from \"recharts\";\n\nimport { ChartContainer } from \"../chart-container\";\nimport { ChartLegendContent } from \"../chart-legend-content\";\nimport { ChartTooltipContent } from \"../chart-tooltip-content\";\nimport { CHART_PALETTE, colorForSeriesIndex } from \"../chart-palette\";\nimport {\n BAR_TOOLTIP_CURSOR_FILL,\n CHART_AXIS_PROPS,\n CHART_GRID_PROPS,\n CHART_MARGIN,\n CHART_VALUE_LABEL_STYLE,\n} from \"../chart-styles\";\nimport { defaultBarTickFormatter } from \"../chart-tick-utils\";\nimport { coerceNumericColumns } from \"../coerce-numeric\";\nimport type { ChartSeries } from \"../types\";\n\nexport interface BarChartProps {\n /** Rows to plot. Each row should carry the `xKey` column and one column per `series[].key`. */\n data: ReadonlyArray<Record<string, unknown>>;\n /** CSV/data column to use for the categorical x-axis (e.g. `\"month\"`, `\"quarter\"`). */\n xKey: string;\n /** One descriptor per bar series. Length 1 = single-series chart; 2+ = grouped or stacked. */\n series: ChartSeries[];\n /**\n * When true, all series stack on one stack-id (vertical sum). When false (default), bars\n * group side-by-side per x-tick.\n */\n stacked?: boolean;\n /** Container height. Number = px, string = any CSS length. Default 320. */\n height?: number | string;\n /** Container width. Default `\"100%\"`. */\n width?: number | string;\n /** Optional className on the outer container. */\n className?: string;\n /** Override the default palette (`CHART_PALETTE`). Colors are assigned by series index. */\n palette?: readonly string[];\n /** Show the tooltip on hover. Default true. */\n showTooltip?: boolean;\n /**\n * Show the legend at the top. **Default behavior**: auto-hidden for single-series charts\n * (the legend would just label the one and only color, which is noise). Pass `true` to force\n * the legend even with one series; pass `false` to suppress for multi-series.\n */\n showLegend?: boolean;\n /** Show horizontal dashed grid lines. Default true. */\n showGrid?: boolean;\n /** Show the x-axis. Default true. */\n showXAxis?: boolean;\n /** Show the y-axis. Default true. */\n showYAxis?: boolean;\n /**\n * Show numeric value labels above each bar. **Default behavior**: auto-on for single\n * non-stacked series (labels stay readable); auto-off for stacked or multi-series bars\n * (labels overlap and clutter). Pass explicitly to override.\n */\n showValueLabels?: boolean;\n /**\n * Recharts XAxis tick `interval`. Default `0` — every category gets a label (no info loss\n * from auto-skipping). Use `\"preserveStartEnd\"` to let recharts drop colliding labels but\n * keep the first and last. For long category names, prefer `xAxisAngle` to rotate the labels\n * rather than dropping them.\n */\n xAxisInterval?: number | \"preserveStart\" | \"preserveEnd\" | \"preserveStartEnd\";\n /**\n * Rotate x-axis tick labels by this many degrees. **Default**: auto — `0` (horizontal) for\n * ≤7 categories; `-30` for 8+ categories so longer labels fit diagonally without overlapping\n * their neighbours. Pass `0` explicitly to force horizontal labels even at high counts; pass\n * any other number to set a custom angle.\n *\n * When the resolved angle is non-zero the chart auto-bumps its bottom margin and right-\n * anchors the labels so they don't run off the axis. The container `height` budget should\n * accommodate the angled labels (~40-60px more).\n */\n xAxisAngle?: number;\n /**\n * Custom formatter for x-axis tick labels. **Default**: clay auto-truncates labels based on\n * the number of categories so no labels overlap (see `getDefaultTickFormatter`). Pass an\n * explicit formatter to override — e.g. for word-boundary truncation, locale-aware\n * abbreviations, or to opt out of truncation entirely with `(v) => String(v)`.\n *\n * The tooltip continues to show the full unformatted value on hover regardless.\n */\n xAxisTickFormatter?: (value: unknown) => string;\n}\n\n/**\n * Pre-composed bar chart. Single-call usage for the common case; for custom recharts trees\n * compose `<ChartContainer>` + recharts primitives directly.\n *\n * ```tsx\n * <BarChart\n * data={[{ month: \"Jan\", us: 120, uk: 60 }, { month: \"Feb\", us: 132, uk: 64 }]}\n * xKey=\"month\"\n * series={[\n * { key: \"us\", label: \"US\" },\n * { key: \"uk\", label: \"UK\" },\n * ]}\n * stacked\n * />\n * ```\n *\n * **Smart defaults**: legend auto-hides for single-series charts; value labels auto-show for\n * single non-stacked series; all x-axis ticks are shown by default (no recharts auto-skip).\n * Override any of these via the named props.\n *\n * Numeric coercion: `data` rows may carry string values for the series columns (e.g. when\n * derived from CSV) — recharts needs numbers, so we coerce in-place. Already-numeric data\n * passes through unchanged.\n */\nexport function BarChart({\n data,\n xKey,\n series,\n stacked = false,\n height = 320,\n width = \"100%\",\n className,\n palette = CHART_PALETTE,\n showTooltip = true,\n showLegend,\n showGrid = true,\n showXAxis = true,\n showYAxis = true,\n showValueLabels,\n xAxisInterval = 0,\n xAxisAngle,\n xAxisTickFormatter,\n}: BarChartProps): ReactNode {\n const seriesKeys = useMemo(() => series.map((s) => s.key), [series]);\n // Memoised so non-artifact consumers passing inline `data` arrays don't re-allocate the\n // coerced copy on every parent render. For the artifact path `data` is already stable from\n // `useRowsArtifactData`, so this is a no-op there.\n const coercedData = useMemo(\n () => coerceNumericColumns(data as ReadonlyArray<Record<string, unknown>>, seriesKeys),\n [data, seriesKeys]\n );\n\n // Smart defaults — see prop JSDoc for rationale.\n const resolvedShowLegend = showLegend ?? series.length > 1;\n const resolvedShowValueLabels = showValueLabels ?? (!stacked && series.length === 1);\n // Auto-angle for dense bar charts: at 8+ categories the horizontal slot per label is too\n // narrow for full-text labels to fit. Rotating to -30° lets labels extend diagonally below\n // the axis without overlapping their neighbours. Consumer can override with `xAxisAngle={0}`.\n const resolvedAngle = xAxisAngle ?? (coercedData.length >= 8 ? -30 : 0);\n // Auto-truncating tick formatter — keeps every bar labeled while preventing overlap. The\n // tooltip shows the full unformatted value on hover, so no information is lost. Consumer\n // can opt out with `(v) => String(v)`.\n const resolvedTickFormatter = useMemo(\n () => xAxisTickFormatter ?? defaultBarTickFormatter(coercedData.length),\n [xAxisTickFormatter, coercedData.length]\n );\n\n // Angled labels need extra bottom margin so they don't clip the chart. ~0.7px per degree\n // covers up to -45° comfortably; we cap to avoid runaway when callers pass aggressive angles.\n const angledLabelPad = resolvedAngle === 0 ? 0 : Math.min(Math.abs(resolvedAngle) * 0.7, 40);\n const chartMargin = { ...CHART_MARGIN, bottom: CHART_MARGIN.bottom + angledLabelPad };\n\n return (\n <ChartContainer height={height} width={width} className={className}>\n <RechartsBarChart data={coercedData} margin={chartMargin} barCategoryGap=\"22%\">\n {showGrid ? <CartesianGrid {...CHART_GRID_PROPS} /> : null}\n {showXAxis ? (\n <XAxis\n dataKey={xKey}\n {...CHART_AXIS_PROPS}\n interval={xAxisInterval}\n angle={resolvedAngle}\n // Right-anchor angled labels so the end of each label aligns with its tick, rather\n // than the centre running off the axis. Middle-anchor for horizontal labels.\n textAnchor={resolvedAngle !== 0 ? \"end\" : \"middle\"}\n tickMargin={resolvedAngle === 0 ? 8 : 12}\n height={resolvedAngle !== 0 ? 60 : undefined}\n tickFormatter={resolvedTickFormatter}\n />\n ) : null}\n {showYAxis ? <YAxis {...CHART_AXIS_PROPS} width={40} /> : null}\n {showTooltip ? <Tooltip cursor={{ fill: BAR_TOOLTIP_CURSOR_FILL }} content={<ChartTooltipContent />} /> : null}\n {resolvedShowLegend ? (\n <Legend\n verticalAlign=\"top\"\n align=\"center\"\n // Pin the absolutely-positioned wrapper flush to the chart's top edge. Recharts'\n // default `top` for a top-aligned legend leaves a gap above the swatches; we'd\n // rather let `CHART_MARGIN.top` control the breathing room below the legend.\n wrapperStyle={{ top: 0 }}\n // Recharts 3.x defaults `itemSorter` to `'value'`, which alphabetically sorts legend\n // entries by label. The tooltip, the stack, and our chart's series colours all follow\n // declaration order — sorting the legend silently breaks correspondence (an \"Others\"\n // series declared last ends up mid-legend alphabetically). `null` disables the sort.\n itemSorter={null}\n content={<ChartLegendContent iconType=\"square\" />}\n />\n ) : null}\n {series.map((s, i) => (\n <Bar\n key={s.key}\n dataKey={s.key}\n name={s.label}\n fill={colorForSeriesIndex(i, palette)}\n stackId={stacked ? \"default\" : s.key}\n radius={barRadius(stacked, i, series.length)}\n >\n {resolvedShowValueLabels ? (\n <LabelList dataKey={s.key} position=\"top\" style={CHART_VALUE_LABEL_STYLE} />\n ) : null}\n </Bar>\n ))}\n </RechartsBarChart>\n </ChartContainer>\n );\n}\n\n/**\n * Per-corner border radius for a bar at position `seriesIndex` in `seriesCount` total series.\n *\n * - **Grouped (non-stacked)** — every series rounded at the top (`[6, 6, 0, 0]`). Bottom sits\n * on the X-axis, so flat bottom looks correct.\n * - **Stacked** — only the top-most segment gets `[6, 6, 0, 0]`; bottom + middle segments are\n * squared so the rect joins look seamless. Bottom segment is flat too because the chart's\n * X-axis is its base.\n */\nfunction barRadius(stacked: boolean, seriesIndex: number, seriesCount: number): [number, number, number, number] {\n if (!stacked) return [6, 6, 0, 0];\n const isTopSegment = seriesIndex === seriesCount - 1;\n return isTopSegment ? [6, 6, 0, 0] : [0, 0, 0, 0];\n}\n","import { type CSSProperties, type ReactElement, type ReactNode } from \"react\";\nimport { ResponsiveContainer } from \"recharts\";\n\nimport { cn } from \"@/lib/utils\";\n\nexport interface ChartContainerProps {\n /** A single recharts chart element — `<LineChart>`, `<BarChart>`, `<PieChart>`, etc. */\n children: ReactElement;\n /**\n * Container height. Recharts' `ResponsiveContainer` measures its parent, so we need a\n * concrete value here (not \"auto\"). Accepts a number (px) or any CSS length string.\n * Defaults to `320` — a sensible mid-density chart height.\n */\n height?: number | string;\n /**\n * Container width. Defaults to `\"100%\"` so the chart flexes to its parent. Pass a fixed\n * number when embedding in a layout that doesn't have a definite width (e.g. an auto-sized\n * flex column).\n */\n width?: number | string;\n /** Optional className spread on the outer wrapper. */\n className?: string;\n /** Optional inline style merged into the outer wrapper. `width`/`height` here take precedence. */\n style?: CSSProperties;\n}\n\n/**\n * The composable foundation for every clay chart. Wraps recharts' `ResponsiveContainer` in a\n * sized `<div>` so the chart has a definite parent box to measure against — without this, charts\n * silently collapse to 0×0 inside flex containers (see the `flex-basis` gotcha documented in\n * the chart-artifact orchestrator).\n *\n * Use directly when composing a chart from recharts primitives:\n *\n * ```tsx\n * <ChartContainer height={320}>\n * <LineChart data={data} margin={{ top: 8, right: 16, left: 0, bottom: 4 }}>\n * <CartesianGrid {...CHART_GRID_PROPS} />\n * <XAxis dataKey=\"month\" {...CHART_AXIS_PROPS} />\n * <YAxis {...CHART_AXIS_PROPS} />\n * <Tooltip content={<ChartTooltipContent />} />\n * <Legend content={<ChartLegendContent />} />\n * <Line dataKey=\"value\" stroke={CHART_PALETTE[0]} />\n * </LineChart>\n * </ChartContainer>\n * ```\n *\n * For common shapes, prefer the pre-composed `<BarChart>` / `<LineChart>` / `<PieChart>` —\n * they call `ChartContainer` under the hood with sensible defaults.\n */\nexport function ChartContainer({\n children,\n height = 320,\n width = \"100%\",\n className,\n style,\n}: ChartContainerProps): ReactNode {\n return (\n <div className={cn(\"text-xs text-foreground\", className)} style={{ width, height, ...style }}>\n <ResponsiveContainer width=\"100%\" height=\"100%\">\n {children}\n </ResponsiveContainer>\n </div>\n );\n}\n","import { type ReactNode } from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\n/**\n * Single entry in recharts' legend payload — one per series. Typed locally because recharts'\n * `LegendProps['payload']` is overly tagged-generic.\n */\ninterface LegendPayloadEntry {\n value?: string | number;\n color?: string;\n dataKey?: string | number;\n type?: string;\n}\n\n/**\n * Recharts injects `payload` at runtime when this component is used as the `content` slot on\n * `<Legend>`. Consumer-facing props (`className`, `iconType`) survive the cloneElement merge.\n */\nexport interface ChartLegendContentProps {\n /** Set by recharts at runtime — one entry per series. */\n payload?: LegendPayloadEntry[];\n /** Optional className merged onto the legend list. */\n className?: string;\n /** Visual indicator next to each label. `square` for bars/pies; `line` for line/area. Default `square`. */\n iconType?: \"square\" | \"line\";\n}\n\n/**\n * The default legend body for clay charts. Drop into any recharts `<Legend>`:\n *\n * ```tsx\n * <Legend content={<ChartLegendContent iconType=\"square\" />} />\n * <Legend content={<ChartLegendContent iconType=\"line\" />} />\n * ```\n *\n * Recharts also accepts `verticalAlign` / `align` on `<Legend>` itself for positioning —\n * `<ChartLegendContent>` is just the body. Style stays consistent regardless of position.\n */\nexport function ChartLegendContent({ payload, className, iconType = \"square\" }: ChartLegendContentProps): ReactNode {\n if (!payload || payload.length === 0) return null;\n\n return (\n <ul\n // No padding on this `<ul>`. Recharts wraps it in a `.recharts-legend-wrapper` div that's\n // positioned `absolute` with an inline `top` — padding here doesn't shift the wrapper's\n // anchor point, it only inflates the wrapper's height. The \"gap above the legend\" must be\n // controlled by the wrapper's inline `top`, set via each chart's `<Legend wrapperStyle>`.\n className={cn(\"flex flex-wrap items-center justify-center gap-x-4 gap-y-2 text-xs text-brown-80\", className)}\n >\n {payload.map((entry: LegendPayloadEntry, i: number) => {\n const swatchColor = typeof entry.color === \"string\" ? entry.color : \"transparent\";\n const label = entry.value !== undefined ? String(entry.value) : \"\";\n return (\n <li key={`${label}-${i}`} className=\"flex items-center gap-1.5\">\n {iconType === \"square\" ? (\n <span className=\"inline-block h-2.5 w-2.5 shrink-0 rounded-sm\" style={{ background: swatchColor }} />\n ) : (\n <span className=\"inline-block h-[3px] w-4 shrink-0 rounded-full\" style={{ background: swatchColor }} />\n )}\n <span>{label}</span>\n </li>\n );\n })}\n </ul>\n );\n}\n","import { type ReactNode } from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\n/**\n * Single entry in recharts' tooltip payload — one per series at the hovered x-tick.\n * Typed locally because recharts' `TooltipProps['payload']` is overly tagged-generic for this use.\n */\ninterface TooltipPayloadEntry {\n name?: string | number;\n value?: number | string;\n color?: string;\n dataKey?: string | number;\n}\n\n/**\n * Recharts injects `active`, `payload`, and `label` at runtime when this component is used as\n * the `content` slot on `<Tooltip>`. Consumer-facing props (`className`, `showLabel`,\n * `formatValue`) survive the cloneElement merge.\n */\nexport interface ChartTooltipContentProps {\n /** Set by recharts at runtime — true when the tooltip is hovered open. */\n active?: boolean;\n /** Set by recharts at runtime — one entry per series. */\n payload?: TooltipPayloadEntry[];\n /** Set by recharts at runtime — the x-tick label for the hovered column. */\n label?: string | number;\n /** Optional className merged onto the tooltip card. */\n className?: string;\n /** When false, the row label (typically the x-tick value) is hidden. Default true. */\n showLabel?: boolean;\n /** Override the formatter used on each entry's value. Defaults to `String(value)`. */\n formatValue?: (value: number | string | undefined, name: string | undefined) => string;\n}\n\n/**\n * The default tooltip card for clay charts. Drop into any recharts `<Tooltip>`:\n *\n * ```tsx\n * <Tooltip content={<ChartTooltipContent />} />\n * <Tooltip content={<ChartTooltipContent formatValue={(v) => `$${v}`} />} />\n * ```\n *\n * Renders a soft brown card with one row per series, swatch left, name center, value right.\n * Returns `null` when recharts says the tooltip is inactive — recharts mounts the content\n * component unconditionally and toggles it via the `active` prop.\n */\nexport function ChartTooltipContent({\n active,\n payload,\n label,\n className,\n showLabel = true,\n formatValue,\n}: ChartTooltipContentProps): ReactNode {\n if (!active || !payload || payload.length === 0) return null;\n\n return (\n <div\n className={cn(\n \"rounded-lg border border-brown-40 bg-brown-10 px-3 py-2 text-xs shadow-sm\",\n \"min-w-32 text-foreground\",\n className\n )}\n >\n {showLabel && label !== undefined && label !== null ? (\n <div className=\"mb-1.5 font-medium text-brown-90\">{String(label)}</div>\n ) : null}\n <ul className=\"space-y-1\">\n {payload.map((entry: TooltipPayloadEntry, i: number) => {\n const name = entry.name !== undefined ? String(entry.name) : \"\";\n const rawValue = entry.value;\n const display = formatValue ? formatValue(rawValue, name) : rawValue === undefined ? \"\" : String(rawValue);\n const swatch = typeof entry.color === \"string\" ? entry.color : \"transparent\";\n return (\n <li key={`${name}-${i}`} className=\"flex items-center gap-2\">\n <span className=\"inline-block h-2.5 w-2.5 shrink-0 rounded-sm\" style={{ background: swatch }} />\n <span className=\"text-brown-70\">{name}</span>\n <span className=\"ml-auto font-semibold tabular-nums text-brown-90\">{display}</span>\n </li>\n );\n })}\n </ul>\n </div>\n );\n}\n","export type { ColorToken } from \"./colors\";\nexport { colors } from \"./colors\";\nexport type { TypographyStyle, TypographyType } from \"./typography\";\nexport { fontFamilies, typographyMixin, typographyStyles, typographyTypes } from \"./typography\";\n","/** Available font families in the Shapes.co design system. */\nexport const fontFamilies = {\n /** Geist — primary UI font for body text, labels, headings, and display. */\n GEIST: \"Geist\",\n /** Crimson Pro — editorial/serif font, always italic. Used for decorative headings and pull quotes. */\n CRIMSON_PRO: \"Crimson Pro\",\n} as const;\n\nexport const typographyTypes = {\n // Geist Label Caption (12px)\n GEIST_LABEL_CAPTION_REGULAR: \"GEIST_LABEL_CAPTION_REGULAR\",\n GEIST_LABEL_CAPTION_MEDIUM: \"GEIST_LABEL_CAPTION_MEDIUM\",\n GEIST_LABEL_CAPTION_SEMI_BOLD: \"GEIST_LABEL_CAPTION_SEMI_BOLD\",\n\n // Geist Body Extra Small (14px)\n GEIST_BODY_XS_REGULAR: \"GEIST_BODY_XS_REGULAR\",\n GEIST_BODY_XS_MEDIUM: \"GEIST_BODY_XS_MEDIUM\",\n GEIST_BODY_XS_BOLD: \"GEIST_BODY_XS_BOLD\",\n\n // Geist Body Small (16px)\n GEIST_BODY_S_REGULAR: \"GEIST_BODY_S_REGULAR\",\n GEIST_BODY_S_MEDIUM: \"GEIST_BODY_S_MEDIUM\",\n GEIST_BODY_S_SEMI_BOLD: \"GEIST_BODY_S_SEMI_BOLD\",\n GEIST_BODY_S_BOLD: \"GEIST_BODY_S_BOLD\",\n\n // Geist Body Medium (18px)\n GEIST_BODY_M_LIGHT: \"GEIST_BODY_M_LIGHT\",\n GEIST_BODY_M_REGULAR: \"GEIST_BODY_M_REGULAR\",\n GEIST_BODY_M_MEDIUM: \"GEIST_BODY_M_MEDIUM\",\n GEIST_BODY_M_SEMI_BOLD: \"GEIST_BODY_M_SEMI_BOLD\",\n\n // Geist Body Large (20px)\n GEIST_BODY_L_REGULAR: \"GEIST_BODY_L_REGULAR\",\n GEIST_BODY_L_MEDIUM: \"GEIST_BODY_L_MEDIUM\",\n GEIST_BODY_L_SEMI_BOLD: \"GEIST_BODY_L_SEMI_BOLD\",\n GEIST_BODY_L_BOLD: \"GEIST_BODY_L_BOLD\",\n\n // Geist Heading Small (24px)\n GEIST_HEADING_S_REGULAR: \"GEIST_HEADING_S_REGULAR\",\n GEIST_HEADING_S_MEDIUM: \"GEIST_HEADING_S_MEDIUM\",\n GEIST_HEADING_S_BOLD: \"GEIST_HEADING_S_BOLD\",\n\n // Geist Heading Medium (28px)\n GEIST_HEADING_M_BOLD: \"GEIST_HEADING_M_BOLD\",\n\n // Geist Heading Large (32px)\n GEIST_HEADING_L_REGULAR: \"GEIST_HEADING_L_REGULAR\",\n GEIST_HEADING_L_MEDIUM: \"GEIST_HEADING_L_MEDIUM\",\n GEIST_HEADING_L_SEMI_BOLD: \"GEIST_HEADING_L_SEMI_BOLD\",\n GEIST_HEADING_L_BOLD: \"GEIST_HEADING_L_BOLD\",\n\n // Geist Display Large (36px)\n GEIST_DISPLAY_L_MEDIUM: \"GEIST_DISPLAY_L_MEDIUM\",\n\n // Crimson Pro Body Small (16px)\n CRIMSON_PRO_BODY_S_LIGHT: \"CRIMSON_PRO_BODY_S_LIGHT\",\n\n // Crimson Pro Body Medium (18px)\n CRIMSON_PRO_BODY_M_LIGHT: \"CRIMSON_PRO_BODY_M_LIGHT\",\n\n // Crimson Pro Heading Medium (24px)\n CRIMSON_PRO_HEADING_M_MEDIUM: \"CRIMSON_PRO_HEADING_M_MEDIUM\",\n\n // Crimson Pro Display Extra Large (46px)\n CRIMSON_PRO_DISPLAY_XL_REGULAR: \"CRIMSON_PRO_DISPLAY_XL_REGULAR\",\n} as const;\n\n/** Union of all typography type string constants. Used as keys into `typographyStyles`. */\nexport type TypographyType = (typeof typographyTypes)[keyof typeof typographyTypes];\n\n/** CSS properties for a single typography type. All numeric values are in pixels. */\nexport interface TypographyStyle {\n /** Font family name (e.g., \"Geist\"). */\n fontFamily: string;\n /** Font size in pixels. */\n fontSize: number;\n /** Font weight (100–900). */\n fontWeight: number;\n /** Line height in pixels. */\n lineHeight: number;\n /** Letter spacing in pixels. Omitted when the font's default spacing is correct. */\n letterSpacing?: number;\n /** CSS font-style value (e.g., \"italic\"). Omitted for normal style. */\n fontStyle?: string;\n}\n\nexport const typographyStyles: Record<TypographyType, TypographyStyle> = {\n // Geist Label Caption (12px)\n [typographyTypes.GEIST_LABEL_CAPTION_REGULAR]: {\n fontFamily: fontFamilies.GEIST,\n fontSize: 12,\n fontWeight: 400,\n lineHeight: 16,\n },\n [typographyTypes.GEIST_LABEL_CAPTION_MEDIUM]: {\n fontFamily: fontFamilies.GEIST,\n fontSize: 12,\n fontWeight: 500,\n lineHeight: 16,\n },\n [typographyTypes.GEIST_LABEL_CAPTION_SEMI_BOLD]: {\n fontFamily: fontFamilies.GEIST,\n fontSize: 12,\n fontWeight: 600,\n lineHeight: 16,\n },\n\n // Geist Body Extra Small (14px)\n [typographyTypes.GEIST_BODY_XS_REGULAR]: {\n fontFamily: fontFamilies.GEIST,\n fontSize: 14,\n fontWeight: 400,\n lineHeight: 20,\n },\n [typographyTypes.GEIST_BODY_XS_MEDIUM]: {\n fontFamily: fontFamilies.GEIST,\n fontSize: 14,\n fontWeight: 500,\n lineHeight: 20,\n },\n [typographyTypes.GEIST_BODY_XS_BOLD]: {\n fontFamily: fontFamilies.GEIST,\n fontSize: 14,\n fontWeight: 600,\n lineHeight: 20,\n },\n\n // Geist Body Small (16px)\n [typographyTypes.GEIST_BODY_S_REGULAR]: {\n fontFamily: fontFamilies.GEIST,\n fontSize: 16,\n fontWeight: 400,\n lineHeight: 24,\n letterSpacing: -0.08,\n },\n [typographyTypes.GEIST_BODY_S_MEDIUM]: {\n fontFamily: fontFamilies.GEIST,\n fontSize: 16,\n fontWeight: 500,\n lineHeight: 24,\n letterSpacing: -0.08,\n },\n [typographyTypes.GEIST_BODY_S_SEMI_BOLD]: {\n fontFamily: fontFamilies.GEIST,\n fontSize: 16,\n fontWeight: 600,\n lineHeight: 24,\n letterSpacing: -0.08,\n },\n [typographyTypes.GEIST_BODY_S_BOLD]: {\n fontFamily: fontFamilies.GEIST,\n fontSize: 16,\n fontWeight: 700,\n lineHeight: 24,\n letterSpacing: -0.08,\n },\n\n // Geist Body Medium (18px)\n [typographyTypes.GEIST_BODY_M_LIGHT]: {\n fontFamily: fontFamilies.GEIST,\n fontSize: 18,\n fontWeight: 300,\n lineHeight: 28,\n letterSpacing: -0.09,\n },\n [typographyTypes.GEIST_BODY_M_REGULAR]: {\n fontFamily: fontFamilies.GEIST,\n fontSize: 18,\n fontWeight: 400,\n lineHeight: 28,\n letterSpacing: -0.09,\n },\n [typographyTypes.GEIST_BODY_M_MEDIUM]: {\n fontFamily: fontFamilies.GEIST,\n fontSize: 18,\n fontWeight: 500,\n lineHeight: 28,\n letterSpacing: -0.09,\n },\n [typographyTypes.GEIST_BODY_M_SEMI_BOLD]: {\n fontFamily: fontFamilies.GEIST,\n fontSize: 18,\n fontWeight: 600,\n lineHeight: 28,\n letterSpacing: -0.09,\n },\n\n // Geist Body Large (20px)\n [typographyTypes.GEIST_BODY_L_REGULAR]: {\n fontFamily: fontFamilies.GEIST,\n fontSize: 20,\n fontWeight: 400,\n lineHeight: 30,\n letterSpacing: -0.2,\n },\n [typographyTypes.GEIST_BODY_L_MEDIUM]: {\n fontFamily: fontFamilies.GEIST,\n fontSize: 20,\n fontWeight: 500,\n lineHeight: 30,\n letterSpacing: -0.2,\n },\n [typographyTypes.GEIST_BODY_L_SEMI_BOLD]: {\n fontFamily: fontFamilies.GEIST,\n fontSize: 20,\n fontWeight: 600,\n lineHeight: 30,\n letterSpacing: -0.2,\n },\n [typographyTypes.GEIST_BODY_L_BOLD]: {\n fontFamily: fontFamilies.GEIST,\n fontSize: 20,\n fontWeight: 700,\n lineHeight: 30,\n letterSpacing: -0.2,\n },\n\n // Geist Heading Small (24px)\n [typographyTypes.GEIST_HEADING_S_REGULAR]: {\n fontFamily: fontFamilies.GEIST,\n fontSize: 24,\n fontWeight: 400,\n lineHeight: 32,\n letterSpacing: -0.48,\n },\n [typographyTypes.GEIST_HEADING_S_MEDIUM]: {\n fontFamily: fontFamilies.GEIST,\n fontSize: 24,\n fontWeight: 500,\n lineHeight: 32,\n letterSpacing: -0.48,\n },\n [typographyTypes.GEIST_HEADING_S_BOLD]: {\n fontFamily: fontFamilies.GEIST,\n fontSize: 24,\n fontWeight: 700,\n lineHeight: 32,\n letterSpacing: -0.48,\n },\n\n // Geist Heading Medium (28px)\n [typographyTypes.GEIST_HEADING_M_BOLD]: {\n fontFamily: fontFamilies.GEIST,\n fontSize: 28,\n fontWeight: 700,\n lineHeight: 36,\n letterSpacing: -0.6,\n },\n\n // Geist Heading Large (32px)\n [typographyTypes.GEIST_HEADING_L_REGULAR]: {\n fontFamily: fontFamilies.GEIST,\n fontSize: 32,\n fontWeight: 400,\n lineHeight: 40,\n letterSpacing: -0.64,\n },\n [typographyTypes.GEIST_HEADING_L_MEDIUM]: {\n fontFamily: fontFamilies.GEIST,\n fontSize: 32,\n fontWeight: 500,\n lineHeight: 40,\n letterSpacing: -0.64,\n },\n [typographyTypes.GEIST_HEADING_L_SEMI_BOLD]: {\n fontFamily: fontFamilies.GEIST,\n fontSize: 32,\n fontWeight: 600,\n lineHeight: 40,\n letterSpacing: -0.64,\n },\n [typographyTypes.GEIST_HEADING_L_BOLD]: {\n fontFamily: fontFamilies.GEIST,\n fontSize: 32,\n fontWeight: 700,\n lineHeight: 40,\n letterSpacing: -0.64,\n },\n\n // Geist Display Large (36px)\n [typographyTypes.GEIST_DISPLAY_L_MEDIUM]: {\n fontFamily: fontFamilies.GEIST,\n fontSize: 36,\n fontWeight: 500,\n lineHeight: 44,\n letterSpacing: -0.72,\n },\n\n // Crimson Pro Body Small (16px)\n [typographyTypes.CRIMSON_PRO_BODY_S_LIGHT]: {\n fontFamily: fontFamilies.CRIMSON_PRO,\n fontSize: 16,\n fontWeight: 300,\n lineHeight: 20,\n letterSpacing: -0.32,\n fontStyle: \"italic\",\n },\n\n // Crimson Pro Body Medium (18px)\n [typographyTypes.CRIMSON_PRO_BODY_M_LIGHT]: {\n fontFamily: fontFamilies.CRIMSON_PRO,\n fontSize: 18,\n fontWeight: 300,\n lineHeight: 28,\n letterSpacing: 0,\n fontStyle: \"italic\",\n },\n\n // Crimson Pro Heading Medium (24px)\n [typographyTypes.CRIMSON_PRO_HEADING_M_MEDIUM]: {\n fontFamily: fontFamilies.CRIMSON_PRO,\n fontSize: 24,\n fontWeight: 400,\n lineHeight: 32,\n letterSpacing: -0.48,\n fontStyle: \"italic\",\n },\n\n // Crimson Pro Display Extra Large (46px)\n [typographyTypes.CRIMSON_PRO_DISPLAY_XL_REGULAR]: {\n fontFamily: fontFamilies.CRIMSON_PRO,\n fontSize: 46,\n fontWeight: 400,\n lineHeight: 54,\n letterSpacing: -1.38,\n fontStyle: \"italic\",\n },\n};\n\n/**\n * Generates a CSS string for a given typography type, suitable for use in styled-components template literals.\n * @param type - The typography type constant (e.g., `typographyTypes.GEIST_BODY_S_REGULAR`).\n * @returns A semicolon-separated CSS string with font-family, font-size, font-weight, line-height, letter-spacing, and font-style.\n */\nexport function typographyMixin(type: TypographyType): string {\n const style = typographyStyles[type];\n return [\n `font-family: \"${style.fontFamily}\", sans-serif`,\n `font-size: ${style.fontSize}px`,\n `font-weight: ${style.fontWeight}`,\n `line-height: ${style.lineHeight}px`,\n `letter-spacing: ${style.letterSpacing != null ? `${style.letterSpacing}px` : \"inherit\"}`,\n `font-style: ${style.fontStyle ?? \"normal\"}`,\n ].join(\";\\n \");\n}\n","import { colors } from \"@/tokens\";\n\n/**\n * Single ordered color palette shared by every chart variant (`BarChart`, `LineChart`,\n * `PieChart`). A graduated walk down the blue family (`blue-600` → `blue-400` → `blue-300` →\n * `blue-100`), pivot into the indigo family (`indigo-400` → `indigo-500` → `indigo-600` →\n * `indigo-800`), and close on the deepest blue (`blue-800`).\n *\n * Design intent: every chart in the product sits in the same cool blue / indigo family so\n * adjacent charts on a dashboard read as a unified system rather than a kaleidoscope. The\n * deliberately narrow hue range (blue + indigo only — no warm accents) is the point: it\n * forecloses on rainbow-coded multi-series charts where the visual noise outweighs the\n * series-distinction it tries to provide.\n *\n * **Same palette for pie too**: pie slices read as parts of a whole, but they also share a\n * dashboard with bar/line charts — keeping one palette across every variant means a single\n * chat surface containing all three variants looks like one design system, not three.\n *\n * Nine entries — the v1 series cap. The palette cycles modulo its length for series counts\n * past nine via `colorForSeriesIndex`.\n */\nexport const CHART_PALETTE = [\n colors[\"blue-600\"], // #2563EB — primary\n colors[\"blue-400\"], // #60A5FA — mid\n colors[\"blue-300\"], // #93C5FD — light\n colors[\"blue-100\"], // #DBEAFE — very light\n colors[\"indigo-400\"], // #818CF8\n colors[\"indigo-500\"], // #6366F1\n colors[\"indigo-600\"], // #4F46E5\n colors[\"indigo-800\"], // #3730A3\n colors[\"blue-800\"], // #1E40AF — deepest\n] as const;\n\n/**\n * Slice color reserved for the \"Others\" bucket in pie charts. Neutral warm grey so it reads as\n * \"leftover / unattributed\" rather than a peer category. Pinning to a constant (rather than the\n * tail of `CHART_PALETTE`) means an Others slice always renders identically regardless of how\n * many named categories the data carries before it.\n */\nexport const OTHERS_SLICE_COLOR = colors[\"brown-60\"];\n\n/**\n * English fallback labels treated as the \"Others\" bucket — case-insensitive match.\n *\n * Consumers should pass their own localized list to `<PieChart>` via the `othersCategoryLabels`\n * prop (or via the artifact's `labels.othersCategoryLabels`) — in non-English UIs the agent\n * writes a translated bucket name (e.g. \"אחרים\", \"Otros\", \"Anderen\") and the consumer's i18n\n * layer is the only place that knows which strings to map to grey/last.\n */\nexport const DEFAULT_OTHERS_CATEGORY_LABELS: readonly string[] = [\"others\", \"other\"];\n\n/**\n * True when `value` matches one of `labels` (case-insensitive, trims whitespace). `labels` is\n * required — consumers pass either `DEFAULT_OTHERS_CATEGORY_LABELS` (English fallback) or their\n * own translated list. Pass an empty array to disable Others detection entirely.\n */\nexport function isOthersCategory(value: unknown, labels: readonly string[]): boolean {\n if (value === null || value === undefined) return false;\n if (labels.length === 0) return false;\n const normalized = String(value).trim().toLowerCase();\n for (const label of labels) {\n if (normalized === label.toLowerCase()) return true;\n }\n return false;\n}\n\n/**\n * Resolves the color for a series at `index`. Cycles deterministically past the palette length\n * so configurations with more series than colors still render rather than throwing. Pass an\n * alternate `palette` (e.g. a brand-specific override) to use something other than the default\n * `CHART_PALETTE`.\n */\nexport function colorForSeriesIndex(index: number, palette: readonly string[] = CHART_PALETTE): string {\n return palette[index % palette.length] ?? CHART_PALETTE[0];\n}\n","import { colors } from \"@/tokens\";\n\n/**\n * Shared visual constants for every chart variant. Two flavours of export:\n *\n * 1. Raw constants (`GRID_STROKE`, `AXIS_STROKE`, …) — for one-off uses or custom recharts trees.\n * 2. Preset prop bundles (`CHART_AXIS_PROPS`, `CHART_GRID_PROPS`) — spreadable into recharts\n * primitives so consumers can build composable charts without restating defaults:\n *\n * ```tsx\n * <CartesianGrid {...CHART_GRID_PROPS} />\n * <XAxis dataKey=\"x\" {...CHART_AXIS_PROPS} tickMargin={8} />\n * ```\n *\n * Recharts primitives must be inlined inside the chart tree — recharts identifies its slot\n * children by component reference (`child.type === XAxis`), so wrapping them in a custom React\n * component breaks dispatch. Constants and preset props are the right shape to share.\n */\n\n// ── raw color constants ──────────────────────────────────────────────────────────────────────\n\n/** Horizontal grid line color — kept soft so the data dominates the visual hierarchy. */\nexport const GRID_STROKE = colors[\"brown-40\"];\n\n/** Axis-line + axis-tick color. Lighter than the tick text so the axis frames rather than competes. */\nexport const AXIS_STROKE = colors[\"brown-50\"];\n\n/** Axis tick label text style. */\nexport const AXIS_TICK_STYLE = {\n fill: colors[\"brown-70\"],\n fontSize: 12,\n fontWeight: 500,\n} as const;\n\n/**\n * Text style for data-point value labels — bars (`<LabelList position=\"top\">`), line\n * data-point labels, and pie slice value labels all use this so labels read as one visual\n * family regardless of chart variant. Matches `AXIS_TICK_STYLE` for color/weight/size so\n * every secondary text element in a chart sits at the same hierarchy level.\n */\nexport const CHART_VALUE_LABEL_STYLE = {\n fill: colors[\"brown-70\"],\n fontSize: 12,\n fontWeight: 500,\n} as const;\n\n/** Cursor (hover guide) fill for bar tooltips. */\nexport const BAR_TOOLTIP_CURSOR_FILL = colors[\"brown-alpha-12\"];\n\n/** Active-dot ring fill on hovered line points — matches the chart background so the dot reads raised. */\nexport const LINE_ACTIVE_DOT_STROKE = colors[\"brown-10\"];\n\n/** Default recharts margin — leaves room for axis ticks/labels and the bottom legend. */\nexport const CHART_MARGIN = { top: 8, right: 16, left: 0, bottom: 4 } as const;\n\n/** Pie-specific margin — extra horizontal room to absorb outside slice labels. */\nexport const PIE_CHART_MARGIN = { top: 0, right: 24, left: 24, bottom: 4 } as const;\n\n// ── preset prop bundles ──────────────────────────────────────────────────────────────────────\n\n/**\n * Spreadable defaults for `<XAxis>` and `<YAxis>` — no axis line, no tick line, soft brown ticks.\n * Pass `dataKey` explicitly on `<XAxis>`; leave it off `<YAxis>`. Override anything per-instance.\n */\nexport const CHART_AXIS_PROPS = {\n stroke: AXIS_STROKE,\n tick: AXIS_TICK_STYLE,\n tickLine: false,\n axisLine: false,\n} as const;\n\n/**\n * Spreadable defaults for `<CartesianGrid>` — horizontal dashed lines only.\n */\nexport const CHART_GRID_PROPS = {\n stroke: GRID_STROKE,\n strokeDasharray: \"3 3\",\n vertical: false,\n} as const;\n","/**\n * Shared x-axis tick helpers used by both `BarChart` and `LineChart`. Pulled into one module so\n * the truncation thresholds and the truncation algorithm itself stay in one place — otherwise\n * each variant evolves its own slightly-different heuristic and the charts read inconsistently\n * side-by-side.\n *\n * Bar uses `defaultBarTickFormatter` (label per category, truncated when dense).\n * Line uses `defaultLineChartTickFormatter` (label only at evenly-distributed indices).\n * Both share the `maxTickCharsByCategoryCount` ladder and the `truncateLabel` ellipsis logic.\n */\n\n/**\n * Max characters per x-axis tick label, keyed by category count. The thresholds approximate the\n * available pixel slot per tick on a typical chat-card-width chart (~700px plot area) at the\n * default 12px tick font size. Sparse charts get no truncation; dense charts get aggressive\n * trimming so labels stop overlapping.\n *\n * Consumers can override the entire formatting via `xAxisTickFormatter` on either chart.\n */\nexport function maxTickCharsByCategoryCount(n: number): number {\n if (n <= 5) return 24; // effectively no truncation\n if (n <= 8) return 14;\n if (n <= 12) return 10;\n if (n <= 16) return 7;\n return 5;\n}\n\n/**\n * Truncates one label per the per-count cap. Trailing whitespace before the ellipsis is trimmed\n * so \"Human Resources\" with cap=7 becomes \"Human…\" not \"Human …\" (reads cleaner).\n */\nexport function truncateLabel(value: unknown, maxChars: number): string {\n const s = String(value ?? \"\");\n if (s.length <= maxChars) return s;\n return s.slice(0, Math.max(1, maxChars - 1)).trimEnd() + \"…\";\n}\n\n/**\n * Returns the set of data-point indices whose label should be visible. First and last always\n * included; the remaining (target - 2) are evenly distributed across the interior. Avoids the\n * \"cluster in the middle\" failure mode of recharts' built-in `preserveStartEnd`.\n *\n * Example for N=12, target=5: visible indices are {0, 3, 5, 8, 11} — 5 evenly-spaced labels.\n * Used by `LineChart` to thin x-axis labels at high data-point counts while keeping every data\n * point drawn.\n */\nexport function evenlyDistributedVisibleIndices(n: number, target: number): Set<number> {\n if (n <= target) {\n return new Set(Array.from({ length: n }, (_, i) => i));\n }\n const set = new Set<number>();\n const step = (n - 1) / (target - 1);\n for (let i = 0; i < target; i++) {\n set.add(Math.round(i * step));\n }\n return set;\n}\n\n/**\n * Per-category truncating formatter used as `BarChart`'s default `tickFormatter`. Every category\n * gets a label (no info loss from auto-skipping) but long labels get truncated by the\n * `maxTickCharsByCategoryCount` ladder so dense charts don't overlap.\n */\nexport function defaultBarTickFormatter(categoryCount: number): (value: unknown) => string {\n const max = maxTickCharsByCategoryCount(categoryCount);\n return (value) => truncateLabel(value, max);\n}\n\n/**\n * Per-index truncating formatter used as `LineChart`'s default `tickFormatter`. Returns the\n * (truncated) label only for indices in `visibleIndices`; the rest return `\"\"` so recharts\n * renders no text there. The data point itself still draws.\n *\n * Truncation is keyed by the *visible* tick count, not the total data-point count: LINE thins\n * its labels via `visibleIndices` (typically 5-7 visible ticks even at N=100+ data points), so\n * each rendered label has plenty of pixel room. Using `categoryCount` here would cap labels\n * like \"Dec 2022\" to 5 characters on a 50-month chart and clip them to \"Dec…\" even though only\n * ~5 ticks actually render. `BarChart` keeps using `categoryCount` because every category gets\n * a tick there — density really is data-point count.\n */\nexport function defaultLineChartTickFormatter(\n categoryCount: number,\n visibleIndices: Set<number>\n): (value: unknown, index: number) => string {\n const max = maxTickCharsByCategoryCount(visibleIndices.size || categoryCount);\n return (value, index) => {\n if (!visibleIndices.has(index)) return \"\";\n return truncateLabel(value, max);\n };\n}\n","/**\n * Returns a shallow copy of `rows` with the `numericKeys` columns coerced to `number`.\n * Cells that can't be parsed become `NaN`; `null` / `undefined` cells pass through untouched;\n * columns NOT in `numericKeys` pass through unchanged.\n *\n * Recharts needs `number` on `dataKey` resolution for `<Line>` / `<Bar>` / `<Pie>` — already-\n * numeric data is a no-op. The function exists for the CSV-derived case where values arrive\n * as strings. `NaN` propagates to recharts as a missing data point (zero-height bar, gap in\n * line, zero-area slice).\n */\nexport function coerceNumericColumns(\n rows: ReadonlyArray<Record<string, unknown>>,\n numericKeys: ReadonlyArray<string>\n): Array<Record<string, unknown>> {\n return rows.map((row) => {\n const out: Record<string, unknown> = { ...row };\n for (const k of numericKeys) {\n const v = out[k];\n if (v == null) continue;\n const n = Number(v);\n out[k] = Number.isFinite(n) ? n : NaN;\n }\n return out;\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, useMemo } from \"react\";\nimport {\n CartesianGrid,\n LabelList,\n Legend,\n Line,\n LineChart as RechartsLineChart,\n Tooltip,\n XAxis,\n YAxis,\n} from \"recharts\";\n\nimport { ChartContainer } from \"../chart-container\";\nimport { ChartLegendContent } from \"../chart-legend-content\";\nimport { ChartTooltipContent } from \"../chart-tooltip-content\";\nimport { CHART_PALETTE, colorForSeriesIndex } from \"../chart-palette\";\nimport {\n CHART_AXIS_PROPS,\n CHART_GRID_PROPS,\n CHART_MARGIN,\n CHART_VALUE_LABEL_STYLE,\n LINE_ACTIVE_DOT_STROKE,\n} from \"../chart-styles\";\nimport { defaultLineChartTickFormatter, evenlyDistributedVisibleIndices } from \"../chart-tick-utils\";\nimport { coerceNumericColumns } from \"../coerce-numeric\";\nimport type { ChartSeries } from \"../types\";\n\nexport interface LineChartProps {\n /** Rows to plot. Each row should carry the `xKey` column and one column per `series[].key`. */\n data: ReadonlyArray<Record<string, unknown>>;\n /** Data column for the categorical/temporal x-axis. */\n xKey: string;\n /** One descriptor per line series. */\n series: ChartSeries[];\n /** Render monotone-curve lines instead of straight segments. **Default true** — smooth\n * curves look cleaner for typical chat / dashboard usage. Pass `false` for true linear\n * interpolation (e.g. step-like data where the literal value at each point matters). */\n smooth?: boolean;\n /** Show data-point dots. **Default true** — dots anchor each bin of the line so the viewer\n * can read individual values at a glance. Pass `false` for a pure trend line (dense data sets\n * where dots would crowd, or styling preference for unembellished strokes). */\n showDots?: boolean;\n /** Line stroke width in px. Default 2. */\n strokeWidth?: number;\n /** Container height. Default 320. */\n height?: number | string;\n /** Container width. Default `\"100%\"`. */\n width?: number | string;\n /** Optional className on the outer container. */\n className?: string;\n /** Override the default palette. Colors are assigned by series index. */\n palette?: readonly string[];\n /** Show the tooltip on hover. Default true. */\n showTooltip?: boolean;\n /**\n * Show the legend at the top. **Default behavior**: auto-hidden for single-series charts\n * (the legend would just label the one and only color, which is noise). Pass `true` to force\n * the legend even with one series; pass `false` to suppress for multi-series.\n */\n showLegend?: boolean;\n /** Show horizontal dashed grid lines. Default true. */\n showGrid?: boolean;\n /** Show the x-axis. Default true. */\n showXAxis?: boolean;\n /** Show the y-axis. Default true. */\n showYAxis?: boolean;\n /**\n * Show numeric value labels next to each data point. **Default off** — for line charts with\n * many data points (12+ months × N series) labels overlap quickly. Set `true` for single-series\n * charts with sparse data (e.g. ≤8 points) where the labels stay readable.\n */\n showValueLabels?: boolean;\n /**\n * Recharts XAxis tick `interval`. **Default**: `0` (every tick rendered) — clay's tick\n * formatter blanks out non-key labels so the visible labels stay evenly distributed.\n * Override to a fixed number to force \"every Nth label\", or to a recharts string preset\n * like `\"preserveStartEnd\"` to let recharts pick.\n */\n xAxisInterval?: number | \"preserveStart\" | \"preserveEnd\" | \"preserveStartEnd\";\n /**\n * Rotate x-axis tick labels by this many degrees. Negative values tilt up-and-right (e.g.\n * `-30`). Default `0`. When non-zero, the chart auto-bumps its bottom margin.\n */\n xAxisAngle?: number;\n /**\n * Custom formatter for x-axis tick labels. **Default**: clay shows only ~5 evenly-distributed\n * labels (first + last + ~3 middle), and auto-truncates each shown label so dense datasets\n * stay readable. Pass an explicit formatter to override the per-label rendering — to also\n * change which labels are visible, pass `xAxisInterval` and a formatter that returns the\n * value for every index.\n *\n * The tooltip continues to show the full unformatted value on hover regardless.\n */\n xAxisTickFormatter?: (value: unknown) => string;\n}\n\n/**\n * Target visible x-axis label count for line charts. Recharts' built-in `preserveStartEnd`\n * picks labels by collision detection, which produces uneven clusters when label widths vary.\n * Picking a fixed target and distributing evenly gives a cleaner result and the same value at\n * any chart width (the chart still shows every data point as a dot — only labels thin out).\n */\nconst TARGET_VISIBLE_LABELS = 5;\n\n/**\n * Pre-composed line chart. Single-call usage for the common case; for custom recharts trees\n * compose `<ChartContainer>` + recharts primitives directly.\n *\n * ```tsx\n * <LineChart\n * data={[{ month: \"Jan\", value: 120 }, { month: \"Feb\", value: 132 }]}\n * xKey=\"month\"\n * series={[{ key: \"value\", label: \"Headcount\" }]}\n * smooth\n * />\n * ```\n *\n * **Smart defaults**: legend auto-hides for single-series; value labels default off (turn on\n * for sparse single-series charts); x-axis shows every tick by default.\n */\nexport function LineChart({\n data,\n xKey,\n series,\n smooth = true,\n showDots = true,\n strokeWidth = 2,\n height = 320,\n width = \"100%\",\n className,\n palette = CHART_PALETTE,\n showTooltip = true,\n showLegend,\n showGrid = true,\n showXAxis = true,\n showYAxis = true,\n showValueLabels = false,\n xAxisInterval = 0,\n xAxisAngle = 0,\n xAxisTickFormatter,\n}: LineChartProps): ReactNode {\n const seriesKeys = useMemo(() => series.map((s) => s.key), [series]);\n // Memoised so non-artifact consumers passing inline `data` arrays don't re-allocate the\n // coerced copy on every parent render.\n const coercedData = useMemo(\n () => coerceNumericColumns(data as ReadonlyArray<Record<string, unknown>>, seriesKeys),\n [data, seriesKeys]\n );\n const lineType = smooth ? \"monotone\" : \"linear\";\n\n const resolvedShowLegend = showLegend ?? series.length > 1;\n // Default: only ~5 evenly distributed labels are visible (first + last + middle), each\n // truncated to fit. Consumer can override with `xAxisTickFormatter` (their formatter is\n // applied to every label without thinning). See `defaultLineChartTickFormatter`.\n const resolvedTickFormatter = useMemo(() => {\n if (xAxisTickFormatter) return xAxisTickFormatter;\n const visible = evenlyDistributedVisibleIndices(coercedData.length, TARGET_VISIBLE_LABELS);\n return defaultLineChartTickFormatter(coercedData.length, visible);\n }, [xAxisTickFormatter, coercedData.length]);\n const angledLabelPad = xAxisAngle === 0 ? 0 : Math.min(Math.abs(xAxisAngle) * 0.7, 40);\n // Extra right margin so the rightmost x-axis tick label has room to render its right half\n // without getting clipped at the chart edge. Recharts centres tick labels under their tick;\n // without padding, ~half of the last label spills past the plot area.\n const chartMargin = { ...CHART_MARGIN, right: CHART_MARGIN.right + 16, bottom: CHART_MARGIN.bottom + angledLabelPad };\n\n return (\n <ChartContainer height={height} width={width} className={className}>\n <RechartsLineChart data={coercedData} margin={chartMargin}>\n {showGrid ? <CartesianGrid {...CHART_GRID_PROPS} /> : null}\n {showXAxis ? (\n <XAxis\n dataKey={xKey}\n {...CHART_AXIS_PROPS}\n interval={xAxisInterval}\n angle={xAxisAngle}\n textAnchor={xAxisAngle !== 0 ? \"end\" : \"middle\"}\n tickMargin={xAxisAngle === 0 ? 8 : 12}\n height={xAxisAngle !== 0 ? 60 : undefined}\n tickFormatter={resolvedTickFormatter}\n />\n ) : null}\n {showYAxis ? <YAxis {...CHART_AXIS_PROPS} width={40} /> : null}\n {showTooltip ? <Tooltip content={<ChartTooltipContent />} /> : null}\n {resolvedShowLegend ? (\n <Legend\n verticalAlign=\"top\"\n align=\"center\"\n // Pin the absolutely-positioned wrapper flush to the chart's top edge — see `BarChart`\n // for the rationale.\n wrapperStyle={{ top: 0 }}\n // Preserve declaration order; disable recharts 3.x's `'value'` alphabetical sort.\n // See `BarChart` for the rationale.\n itemSorter={null}\n content={<ChartLegendContent iconType=\"line\" />}\n />\n ) : null}\n {series.map((s, i) => {\n const color = colorForSeriesIndex(i, palette);\n return (\n <Line\n key={s.key}\n type={lineType}\n dataKey={s.key}\n name={s.label}\n stroke={color}\n strokeWidth={strokeWidth}\n // Filled dot matching the line color — recharts default is hollow (white fill +\n // colored stroke), which reads as \"annotation\" rather than \"data point\".\n dot={showDots ? { r: 3, fill: color, stroke: color, strokeWidth: 0 } : false}\n activeDot={{ r: 5, fill: color, stroke: LINE_ACTIVE_DOT_STROKE, strokeWidth: 2 }}\n >\n {showValueLabels ? <LabelList dataKey={s.key} position=\"top\" style={CHART_VALUE_LABEL_STYLE} /> : null}\n </Line>\n );\n })}\n </RechartsLineChart>\n </ChartContainer>\n );\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, useMemo } from \"react\";\nimport { Cell, Legend, Pie, PieChart as RechartsPieChart, Tooltip } from \"recharts\";\n\nimport { ChartContainer } from \"../chart-container\";\nimport { ChartLegendContent } from \"../chart-legend-content\";\nimport { ChartTooltipContent } from \"../chart-tooltip-content\";\nimport {\n CHART_PALETTE,\n DEFAULT_OTHERS_CATEGORY_LABELS,\n OTHERS_SLICE_COLOR,\n colorForSeriesIndex,\n isOthersCategory,\n} from \"../chart-palette\";\nimport { CHART_VALUE_LABEL_STYLE, PIE_CHART_MARGIN } from \"../chart-styles\";\nimport { coerceNumericColumns } from \"../coerce-numeric\";\n\n/**\n * Recharts injects geometric position props onto label render functions. Typed locally because\n * recharts' `PieLabelProps` is overly tagged-generic.\n */\ninterface PieLabelRenderProps {\n /** Computed x coordinate for the label (outside the slice). */\n x?: number;\n /** Computed y coordinate for the label. */\n y?: number;\n /** \"start\" / \"middle\" / \"end\" / \"inherit\" — pre-computed for legibility outside the slice. */\n textAnchor?: \"start\" | \"middle\" | \"end\" | \"inherit\";\n /** The slice's numeric value (the value of `valueKey` on the row). */\n value?: unknown;\n}\n\nexport interface PieChartProps {\n /** Rows to plot. Each row maps to one slice. */\n data: ReadonlyArray<Record<string, unknown>>;\n /** Data column used as the slice label / legend entry. */\n categoryKey: string;\n /** Data column used as the slice numeric value. */\n valueKey: string;\n /** Container height. Default 320. */\n height?: number | string;\n /** Container width. Default `\"100%\"`. */\n width?: number | string;\n /** Optional className on the outer container. */\n className?: string;\n /** Override the default palette. Default `CHART_PALETTE` — the shared blue + indigo graduated\n * palette every chart variant uses. Override per-instance for brand-specific colours. */\n palette?: readonly string[];\n /** Show value labels next to each slice. Default true. */\n showLabels?: boolean;\n /** Show the tooltip on hover. Default true. */\n showTooltip?: boolean;\n /** Show the legend (category names). Default true. Position controlled by `legendPosition`. */\n showLegend?: boolean;\n /** Inner radius — `0` (default) for a pie; `>0` (e.g. `\"40%\"`) for a donut. */\n innerRadius?: number | string;\n /** Outer radius. Default `\"68%\"` — leaves room for outside value labels. */\n outerRadius?: number | string;\n /**\n * Category labels treated as the \"Others\" bucket (case-insensitive match). Matching slices are\n * pinned to the end of the sweep regardless of value and rendered in neutral grey\n * (`OTHERS_SLICE_COLOR`).\n *\n * **Default**: `DEFAULT_OTHERS_CATEGORY_LABELS` (English \"Others\" / \"Other\"). Pass a localized\n * list in non-English UIs — e.g. `[\"אחרים\"]` for Hebrew or `[\"Otros\", \"Otro\"]` for Spanish.\n * Pass `[]` to disable Others detection entirely (every slice colored from the palette).\n */\n othersCategoryLabels?: readonly string[];\n}\n\n/**\n * Pre-composed pie chart. One slice per row, colored by row index from the palette. Outside\n * value labels (no connector line); legend at the top. Set `innerRadius` to make a donut.\n *\n * **Sweep**: starts at 12 o'clock and proceeds clockwise (`startAngle={90}` / `endAngle={-270}`).\n * Recharts' default starts at 3 o'clock counter-clockwise, which feels arbitrary for a\n * data-first composition.\n *\n * **\"Others\" bucket**: any slice whose category label matches \"Others\" / \"Other\"\n * (case-insensitive — see `isOthersCategory`) is pinned to the end of the sweep regardless of\n * value, and rendered in neutral grey (`OTHERS_SLICE_COLOR`) so it reads as \"unattributed\n * leftover\" rather than a peer category. Other slices retain their incoming order.\n *\n * ```tsx\n * <PieChart\n * data={[\n * { department: \"Engineering\", headcount: 42 },\n * { department: \"Sales\", headcount: 18 },\n * { department: \"Others\", headcount: 5 },\n * ]}\n * categoryKey=\"department\"\n * valueKey=\"headcount\"\n * />\n *\n * // Donut variant\n * <PieChart data={...} categoryKey=\"...\" valueKey=\"...\" innerRadius=\"40%\" />\n * ```\n */\nexport function PieChart({\n data,\n categoryKey,\n valueKey,\n height = 320,\n width = \"100%\",\n className,\n palette = CHART_PALETTE,\n showLabels = true,\n showTooltip = true,\n showLegend = true,\n innerRadius = 0,\n outerRadius = \"68%\",\n othersCategoryLabels = DEFAULT_OTHERS_CATEGORY_LABELS,\n}: PieChartProps): ReactNode {\n // Memoised so non-artifact consumers passing inline `data` arrays don't re-allocate the\n // coerced + reordered copy on every parent render. Three steps:\n // 1. Coerce the `valueKey` column to numeric (CSV-derived rows arrive as strings).\n // 2. Stable-partition: keep non-Others slices in their original order, then append any\n // Others slices at the tail — preserves the agent's intended ordering for the named\n // categories while guaranteeing \"Others\" sits at the end of the sweep regardless of size.\n // 3. Compute per-slice colors keyed by the **named-slice index** (not the position in the\n // reordered array). Without this, an Others row added or removed in the middle of the\n // data would visibly shift the palette colors of every named slice that came after it.\n // Indexing by \"Nth named slice\" keeps each named category's color stable across data\n // edits / `othersCategoryLabels` updates.\n const { orderedData, sliceColors } = useMemo(() => {\n const coerced = coerceNumericColumns(data as ReadonlyArray<Record<string, unknown>>, [valueKey]);\n const namedRows: Record<string, unknown>[] = [];\n const namedColors: string[] = [];\n const othersRows: Record<string, unknown>[] = [];\n const othersColors: string[] = [];\n for (const row of coerced) {\n if (isOthersCategory(row[categoryKey], othersCategoryLabels)) {\n othersRows.push(row);\n othersColors.push(OTHERS_SLICE_COLOR);\n } else {\n namedRows.push(row);\n namedColors.push(colorForSeriesIndex(namedRows.length - 1, palette));\n }\n }\n return {\n orderedData: [...namedRows, ...othersRows],\n sliceColors: [...namedColors, ...othersColors],\n };\n }, [data, valueKey, categoryKey, othersCategoryLabels, palette]);\n\n return (\n <ChartContainer height={height} width={width} className={className}>\n <RechartsPieChart margin={PIE_CHART_MARGIN}>\n {showTooltip ? <Tooltip content={<ChartTooltipContent />} /> : null}\n {showLegend ? (\n <Legend\n verticalAlign=\"top\"\n align=\"center\"\n // Pin the absolutely-positioned wrapper flush to the chart's top edge — see `BarChart`\n // for the rationale.\n wrapperStyle={{ top: 0 }}\n // Preserve slice order; disable recharts 3.x's `'value'` alphabetical sort. Critical\n // for pie because the Others bucket is intentionally pinned to the end of the sweep\n // — sorting alphabetically would break that contract.\n itemSorter={null}\n content={<ChartLegendContent iconType=\"square\" />}\n />\n ) : null}\n <Pie\n data={orderedData}\n dataKey={valueKey}\n nameKey={categoryKey}\n cx=\"50%\"\n cy=\"52%\"\n // Start at 12 o'clock and sweep clockwise. Recharts' default (`0` / `360`) starts at\n // 3 o'clock counter-clockwise. The +90 / -270 pair preserves the full 360° arc length.\n startAngle={90}\n endAngle={-270}\n innerRadius={innerRadius}\n outerRadius={outerRadius}\n label={\n showLabels\n ? (entry: PieLabelRenderProps) => {\n const v = entry.value;\n if (v === undefined || v === null) return null;\n // Render the label as a `<text>` so we can pin its `fill` to the shared\n // `CHART_VALUE_LABEL_STYLE` instead of letting recharts auto-color it to\n // the slice's fill (which on light-blue slices becomes near-invisible).\n // Recharts passes the computed `x`/`y`/`textAnchor` so positioning is\n // unchanged.\n return (\n <text\n x={entry.x}\n y={entry.y}\n fill={CHART_VALUE_LABEL_STYLE.fill}\n fontSize={CHART_VALUE_LABEL_STYLE.fontSize}\n fontWeight={CHART_VALUE_LABEL_STYLE.fontWeight}\n textAnchor={entry.textAnchor}\n dominantBaseline=\"central\"\n >\n {String(v)}\n </text>\n );\n }\n : false\n }\n // No connector line — outside labels sit clean next to their slice. The thin\n // brown line between slice and label adds visual noise without information gain.\n labelLine={false}\n // No stroke between slices — gradient palette + adjacent contiguous segments read\n // as one continuous wheel rather than discrete cards with cream gaps between them.\n stroke=\"none\"\n strokeWidth={0}\n >\n {orderedData.map((_row, i) => (\n <Cell key={i} fill={sliceColors[i]} />\n ))}\n </Pie>\n </RechartsPieChart>\n </ChartContainer>\n );\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 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 { 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 * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\n/** Extension of shadcn's `Table` registry component: exposes `containerClassName` so callers can\n * override the wrapping `<div>`'s default `overflow-auto`. We need this for sticky-header support\n * (sticky uses the nearest overflow ancestor as its containing block — when the actual scroll\n * container is one level out, the inner overflow-auto needs to be neutralised with\n * `overflow-visible` so sticky aligns with the real scrolling parent). */\nconst Table = React.forwardRef<\n HTMLTableElement,\n React.HTMLAttributes<HTMLTableElement> & { containerClassName?: string }\n>(({ className, containerClassName, ...props }, ref) => (\n <div className={cn(\"relative w-full overflow-auto\", containerClassName)}>\n <table ref={ref} className={cn(\"w-full caption-bottom text-sm\", className)} {...props} />\n </div>\n));\nTable.displayName = \"Table\";\n\nconst TableHeader = React.forwardRef<HTMLTableSectionElement, React.HTMLAttributes<HTMLTableSectionElement>>(\n ({ className, ...props }, ref) => <thead ref={ref} className={cn(\"[&_tr]:border-b\", className)} {...props} />\n);\nTableHeader.displayName = \"TableHeader\";\n\nconst TableBody = React.forwardRef<HTMLTableSectionElement, React.HTMLAttributes<HTMLTableSectionElement>>(\n ({ className, ...props }, ref) => (\n <tbody ref={ref} className={cn(\"[&_tr:last-child]:border-0\", className)} {...props} />\n )\n);\nTableBody.displayName = \"TableBody\";\n\nconst TableFooter = React.forwardRef<HTMLTableSectionElement, React.HTMLAttributes<HTMLTableSectionElement>>(\n ({ className, ...props }, ref) => (\n <tfoot ref={ref} className={cn(\"border-t bg-muted/50 font-medium [&>tr]:last:border-b-0\", className)} {...props} />\n )\n);\nTableFooter.displayName = \"TableFooter\";\n\nconst TableRow = React.forwardRef<HTMLTableRowElement, React.HTMLAttributes<HTMLTableRowElement>>(\n ({ className, ...props }, ref) => (\n <tr\n ref={ref}\n className={cn(\"border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted\", className)}\n {...props}\n />\n )\n);\nTableRow.displayName = \"TableRow\";\n\nconst TableHead = React.forwardRef<HTMLTableCellElement, React.ThHTMLAttributes<HTMLTableCellElement>>(\n ({ className, ...props }, ref) => (\n <th\n ref={ref}\n className={cn(\n \"h-12 px-4 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0\",\n className\n )}\n {...props}\n />\n )\n);\nTableHead.displayName = \"TableHead\";\n\nconst TableCell = React.forwardRef<HTMLTableCellElement, React.TdHTMLAttributes<HTMLTableCellElement>>(\n ({ className, ...props }, ref) => (\n <td ref={ref} className={cn(\"p-4 align-middle [&:has([role=checkbox])]:pr-0\", className)} {...props} />\n )\n);\nTableCell.displayName = \"TableCell\";\n\nconst TableCaption = React.forwardRef<HTMLTableCaptionElement, React.HTMLAttributes<HTMLTableCaptionElement>>(\n ({ className, ...props }, ref) => (\n <caption ref={ref} className={cn(\"mt-4 text-sm text-muted-foreground\", className)} {...props} />\n )\n);\nTableCaption.displayName = \"TableCaption\";\n\nexport { Table, TableHeader, TableBody, TableFooter, TableHead, TableRow, TableCell, TableCaption };\n","import { useEffect, useRef, useState, type RefObject } from \"react\";\n\n/**\n * Tracks whether a scroll container has scrolled past its top edge, for sticky-header shadow effects.\n *\n * Returns `targetRef` (attach to the scroll container itself or any element inside it) and\n * `isScrolled` (true once the container's `scrollTop > 0`). On mount the hook walks up the DOM\n * from the target to find the nearest overflow ancestor (or uses the target itself if it already\n * has `overflowY: auto | scroll`), then listens to its `scroll` event.\n *\n * Used to surface a subtle \"scroll lift\" shadow under sticky headers so users know there is\n * content hidden above.\n *\n * **Why a scroll listener and not a sentinel + IntersectionObserver:** an earlier version used a\n * 1px sentinel as the first child of the scroll container. The sentinel's height pushed the thead\n * down by one pixel in normal flow, and as soon as the user scrolled the thead snapped up to its\n * sticky position — a visible 1px jitter. A scroll listener has no layout footprint.\n *\n * `passive: true` so the listener doesn't block native scroll smoothness; we never call\n * `preventDefault`.\n */\nexport function useScrollShadow(): {\n targetRef: RefObject<HTMLDivElement | null>;\n isScrolled: boolean;\n} {\n const targetRef = useRef<HTMLDivElement>(null);\n const [isScrolled, setIsScrolled] = useState(false);\n\n useEffect(() => {\n const target = targetRef.current;\n if (!target) return;\n\n let scrollContainer: HTMLElement | null = target;\n while (scrollContainer) {\n const overflowY = getComputedStyle(scrollContainer).overflowY;\n if (overflowY === \"auto\" || overflowY === \"scroll\") break;\n scrollContainer = scrollContainer.parentElement;\n }\n if (!scrollContainer) return;\n\n setIsScrolled(scrollContainer.scrollTop > 0);\n\n const handleScroll = () => {\n if (!scrollContainer) return;\n setIsScrolled(scrollContainer.scrollTop > 0);\n };\n scrollContainer.addEventListener(\"scroll\", handleScroll, { passive: true });\n return () => scrollContainer.removeEventListener(\"scroll\", handleScroll);\n }, []);\n\n return { targetRef, isScrolled };\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 { 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 { 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 { 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 { 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};\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","import type { ComponentType, ReactNode } from \"react\";\n\nimport { ArtifactServices } from \"@/components/artifacts/artifact-services\";\nimport type { ArtifactRefBlockRecord, BlockComponentProps } from \"../types\";\n\n/**\n * Renders an `ARTIFACT_REF` block by dispatching the inlined artifact to its per-type component.\n *\n * The cast at the seam is necessary because the registry-based dispatch can't statically prove\n * type-T narrowing — but it's not hiding a bug. `ArtifactRefBlockRecord` is a distributed union\n * over `ArtifactRecord` branches (see `blocks/types.ts`), so TS catches wrong-shape labels at the\n * consumer's construction site: a chart-labels bag assigned to a TABLE block's payload is a type\n * error there, not silently forwarded to the wrong renderer here.\n *\n * Unknown artifact types render as `null` — the map only contains types we know how to render,\n * so an older clay client receiving a future artifact type degrades silently rather than\n * crashing the chat.\n */\nexport function ArtifactRefBlock({ block }: BlockComponentProps<ArtifactRefBlockRecord>): ReactNode {\n const { artifact, labels, callbacks } = block.payload;\n const entry = ArtifactServices[artifact.type];\n if (!entry) return null;\n // The registry stores `ArtifactService<ArtifactRecord>` (the union), so `entry.Component` is\n // `ComponentType<ArtifactServiceProps<ArtifactRecord>>`. The labels/callbacks props in the\n // narrower per-type `ArtifactServiceProps<TArtifact>` are `Partial<ArtifactLabelsOf<TArtifact>>`\n // which the union doesn't satisfy positionally. Cast forwards the values verbatim — the\n // runtime invariant is provably correct because the distributed `ArtifactRefBlockRecord` type\n // enforces it at construction.\n const Component = entry.Component as ComponentType<{\n artifact: typeof artifact;\n labels?: typeof labels;\n callbacks?: typeof callbacks;\n }>;\n return <Component artifact={artifact} labels={labels} callbacks={callbacks} />;\n}\n","import { artifactToClipboardText } from \"@/components/artifacts/artifact-services\";\nimport { ArtifactRefBlock } from \"../artifact-ref-block/artifact-ref-block\";\nimport { blockTypes } from \"../types\";\nimport type { ArtifactRefBlockRecord, BlockService } from \"../types\";\n\nexport const ArtifactRefBlockService: BlockService<ArtifactRefBlockRecord> = {\n type: blockTypes.ARTIFACT_REF,\n Component: ArtifactRefBlock,\n /**\n * Delegates to the registered artifact component's `toClipboardText`. Falls back to\n * `[Artifact: <title>]` via `artifactToClipboardText`'s own fallback when the artifact type\n * has no registered component, so copy never silently produces an empty string.\n */\n toClipboardText: (block) => artifactToClipboardText(block.payload.artifact),\n};\n","const PLACEHOLDER_PREFIX = \"\\u200B\\u200BIC\";\nconst PLACEHOLDER_SUFFIX = \"\\u200B\\u200B\";\nconst PLACEHOLDER_REGEX = new RegExp(`${PLACEHOLDER_PREFIX}(\\\\d+)${PLACEHOLDER_SUFFIX}`, \"g\");\n\n/**\n * Strips only inline markdown marks (bold, italic, strikethrough) from a string.\n * Does not touch block-level syntax (headings, blockquotes, horizontal rules, links, images)\n * so values like `> 100`, `# items`, or `---` are preserved as-is.\n * @param text - The text to strip inline marks from.\n * @returns The text with inline marks removed.\n */\nexport function stripInlineMarks(text: string): string {\n return (\n text\n // Bold + italic\n .replace(/\\*{3}(.+?)\\*{3}/g, \"$1\")\n // Bold\n .replace(/\\*{2}(.+?)\\*{2}/g, \"$1\")\n .replace(/_{2}(.+?)_{2}/g, \"$1\")\n // Italic\n .replace(/\\*(.+?)\\*/g, \"$1\")\n .replace(/_(.+?)_/g, \"$1\")\n // Strikethrough\n .replace(/~~(.+?)~~/g, \"$1\")\n );\n}\n\n/**\n * Converts a Markdown string to plain text by stripping all formatting syntax.\n *\n * Handles headings, bold/italic/strikethrough, links, images, blockquotes, code blocks,\n * inline code, tables, and horizontal rules. Inline code content is preserved literally\n * (formatting inside backticks is not stripped).\n *\n * @param markdown - The Markdown source string.\n * @returns A plain-text string with all Markdown syntax removed.\n */\nexport function markdownToPlainText(markdown: string): string {\n if (!markdown) return \"\";\n\n // Protect inline code content from bold/italic stripping by replacing with placeholders\n const inlineCodeSnippets: string[] = [];\n let result = markdown\n // HTML comments (e.g. <!-- table-title: ... -->), including multiline and --!> variants\n .replace(/^<!--[\\s\\S]*?--!?>\\n?/gm, \"\")\n // Code blocks (must come first)\n .replace(/```[\\s\\S]*?```/g, \"\")\n // Inline code → placeholder\n .replace(/`([^`]+)`/g, (_match, code: string) => {\n inlineCodeSnippets.push(code);\n return `${PLACEHOLDER_PREFIX}${inlineCodeSnippets.length - 1}${PLACEHOLDER_SUFFIX}`;\n })\n // Remove headings markers, keep text\n .replace(/^#{1,6}\\s+/gm, \"\")\n // Remove horizontal rules\n .replace(/^(?:---|\\*\\*\\*|___)\\s*$/gm, \"\");\n result = stripInlineMarks(result)\n // Images: ![alt](url) → alt (must come before links)\n .replace(/!\\[([^\\]]*)\\]\\([^)]+\\)/g, \"$1\")\n // Links: [text](url) → text\n .replace(/\\[([^\\]]+)\\]\\([^)]+\\)/g, \"$1\")\n // Blockquote markers\n .replace(/^>\\s?/gm, \"\")\n // Table separator rows (|---|---|)\n .replace(/^\\|[-:\\s|]+\\|\\s*$/gm, \"\")\n // Table rows: | a | b | → a | b\n .replace(/^\\|\\s?/gm, \"\")\n .replace(/\\s?\\|$/gm, \"\")\n // Collapse multiple blank lines\n .replace(/\\n{3,}/g, \"\\n\\n\")\n .trim();\n\n // Restore inline code content\n result = result.replace(PLACEHOLDER_REGEX, (_match, index: string) => inlineCodeSnippets[Number(index)]);\n\n return result;\n}\n","import type { ReactNode } from \"react\";\nimport ReactMarkdown from \"react-markdown\";\nimport remarkBreaks from \"remark-breaks\";\nimport remarkGfm from \"remark-gfm\";\n\nimport { ScrollableTable } from \"../scrollable-table/scrollable-table\";\nimport type { BlockComponentProps, TextBlockRecord } from \"../types\";\nimport { sanitize } from \"./sanitize\";\nimport { TextBlockWrapper } from \"./text-block-styles\";\n\nconst markdownComponents = {\n table: ((tableProps: { node?: unknown; children?: ReactNode }) => (\n <ScrollableTable {...tableProps} />\n )) as React.ComponentType<unknown>,\n};\n\n/** Renders a TEXT block as Markdown with GFM (tables, strikethrough) and line breaks. */\nexport function TextBlock({ block }: BlockComponentProps<TextBlockRecord>): ReactNode {\n return (\n <TextBlockWrapper>\n <ReactMarkdown remarkPlugins={[remarkGfm, remarkBreaks]} components={markdownComponents}>\n {sanitize(block.payload.text)}\n </ReactMarkdown>\n </TextBlockWrapper>\n );\n}\n","import styled from \"styled-components\";\n\nexport const TableContainer = styled.div`\n position: relative;\n margin-block: 8px 16px;\n`;\n\nexport const TableScroll = styled.div`\n width: 100%;\n overflow-x: auto;\n overflow-y: hidden;\n`;\n","import { type ReactNode } from \"react\";\n\nimport { TableContainer, TableScroll } from \"./scrollable-table-styles\";\n\n/** Props for the ScrollableTable component. */\ninterface ScrollableTableProps {\n /** Unused node prop from react-markdown. */\n node?: unknown;\n /** Table children elements. */\n children?: ReactNode;\n}\n\n/** Renders a markdown table inside a horizontally scrollable wrapper. Read-only — actions live on artifact-tables. */\nexport function ScrollableTable({ node: _node, ...props }: ScrollableTableProps): ReactNode {\n return (\n <TableContainer>\n <TableScroll>\n <table {...props} />\n </TableScroll>\n </TableContainer>\n );\n}\n","// Strips io-server's `<!-- table-title: ... -->` HTML-comment metadata before rendering.\n// The marker is emitted by the server's CSV-export pipeline so the export button can label\n// the file; it has no place in the visual output. If the server-side format changes\n// (prefix, spacing, closing-tag style), this filter silently stops matching and the\n// raw HTML comment leaks into rendered Markdown — keep this contract aligned with io-server.\nexport function sanitize(text: string): string {\n return text\n .split(\"\\n\")\n .filter((line) => {\n const t = line.trim();\n return !(t.startsWith(\"<!-- table-title:\") && t.endsWith(\"-->\"));\n })\n .join(\"\\n\");\n}\n","import styled from \"styled-components\";\n\nimport { colors } from \"@/tokens/colors\";\nimport { typographyMixin, typographyTypes } from \"@/tokens/typography\";\n\nexport const TextBlockWrapper = styled.div`\n ${typographyMixin(typographyTypes.GEIST_BODY_S_REGULAR)};\n color: ${colors[\"brown-100\"]};\n word-break: break-word;\n\n & h1 {\n ${typographyMixin(typographyTypes.GEIST_HEADING_S_BOLD)};\n margin-block: 20px;\n }\n\n & h2 {\n ${typographyMixin(typographyTypes.GEIST_BODY_L_SEMI_BOLD)};\n margin-block: 12px;\n }\n\n & h3,\n & h4,\n & h5,\n & h6 {\n ${typographyMixin(typographyTypes.GEIST_BODY_M_SEMI_BOLD)};\n margin-block: 8px;\n }\n\n & strong {\n ${typographyMixin(typographyTypes.GEIST_BODY_S_SEMI_BOLD)};\n }\n\n & p {\n margin-block: 0px;\n }\n\n & p + p {\n margin-block: 8px 0px;\n }\n\n & ul,\n & ol {\n margin-block: 16px;\n padding-left: 20px;\n }\n\n & li + li {\n margin-block: 4px;\n }\n\n & ol {\n list-style-type: decimal;\n }\n\n & ol ol {\n list-style-type: lower-alpha;\n }\n\n & ol ol ol {\n list-style-type: lower-roman;\n }\n\n & a {\n color: inherit;\n text-decoration-line: underline;\n text-decoration-style: dotted;\n text-decoration-thickness: auto;\n text-underline-offset: auto;\n text-underline-position: from-font;\n transition: color 75ms ease-in-out;\n\n &:hover {\n color: ${colors[\"blue-600\"]};\n }\n }\n\n & hr {\n border: none;\n border-top: 1px solid ${colors[\"brown-40\"]};\n margin-block: 20px;\n }\n\n & table {\n margin: 0;\n width: 100%;\n border-collapse: collapse;\n }\n\n & th,\n & td {\n border: none;\n border-bottom: 1px solid ${colors[\"brown-40\"]};\n text-align: left;\n }\n\n & th {\n ${typographyMixin(typographyTypes.GEIST_LABEL_CAPTION_MEDIUM)};\n padding: 8px;\n }\n\n & td {\n ${typographyMixin(typographyTypes.GEIST_BODY_XS_MEDIUM)};\n padding: 16px 8px;\n min-width: 150px;\n width: 1%;\n }\n\n & td strong {\n font-weight: inherit;\n font-size: inherit;\n line-height: inherit;\n }\n\n & blockquote {\n margin-block: 4px;\n margin-inline: 0px;\n padding-left: 16px;\n border-left: 4px solid ${colors[\"brown-40\"]};\n color: ${colors[\"brown-80\"]};\n }\n`;\n","import { markdownToPlainText } from \"@/utils/markdown-to-plain-text\";\nimport { TextBlock } from \"../text-block/text-block\";\nimport { blockTypes } from \"../types\";\nimport type { BlockService, TextBlockRecord } from \"../types\";\n\nexport const TextBlockService: BlockService<TextBlockRecord> = {\n type: blockTypes.TEXT,\n Component: TextBlock,\n /**\n * Converts the block's markdown source to plain text so the final clipboard value is clean\n * when pasted into tools that don't render markdown. Each block type owns its own clipboard\n * conversion so `useCopyToClipboard` can write text verbatim without a second pass.\n */\n toClipboardText: (block) => markdownToPlainText(block.payload.text),\n};\n","import { blockTypes, type BlockService, type BlockType } from \"../types\";\nimport { ArtifactRefBlockService } from \"./artifact-ref-block-service\";\nimport { TextBlockService } from \"./text-block-service\";\n\n// `BlockService<TBlock>` is invariant on `TBlock` via React's `ComponentType` (props are\n// contravariant). The concrete services overlap structurally with the base `BlockService` type\n// enough for a direct `as BlockService` cast. The registry dispatches by runtime discriminator —\n// `BlockServices[block.type]` always resolves to the matching concrete service — so the cast is sound.\nexport const BlockServices: Record<BlockType, BlockService> = {\n [blockTypes.TEXT]: TextBlockService as BlockService,\n [blockTypes.ARTIFACT_REF]: ArtifactRefBlockService as BlockService,\n};\n","import type { ReactNode } from \"react\";\n\nimport { BlockServices } from \"../block-services\";\nimport type { BlockRecord } from \"../types\";\n\n/** Props for the Block dispatcher. */\ninterface BlockProps {\n /** The block to render. Dispatched to the registered concrete component for its `type`. */\n block: BlockRecord;\n}\n\n/**\n * Dispatches a single block to its registered concrete component. Unknown types render as a no-op.\n *\n * Generic by design: per-block-type extras (e.g. localized artifact labels for `ARTIFACT_REF`)\n * live on the block's `payload`, not as sibling props here. That keeps the dispatcher's contract\n * shallow — third-party `BlockService` implementors only need to handle `{ block }` — and lets\n * the consumer materialise a fully-decorated block once (from the server payload + their i18n\n * layer) instead of threading sibling props through every dispatch site.\n */\nexport function Block({ block }: BlockProps): ReactNode {\n const service = BlockServices[block.type];\n if (!service) return null;\n const { Component } = service;\n return <Component block={block} />;\n}\n","import { BlockServices } from \"@/components/blocks/block-services\";\nimport type { ChatMessage } from \"../types\";\n\n/**\n * Aggregates the clipboard text for all blocks in a message.\n *\n * Iterates `message.blocks` in order, calls `BlockServices[block.type].toClipboardText(block)`\n * for each, and joins the results with `\"\\n\\n\"` so multi-block messages have a blank line between\n * each section (matching the visual gap rendered between blocks in the chat UI).\n *\n * Block-type-specific behaviour:\n * - `TEXT` blocks: markdown stripped to plain text via `markdownToPlainText`.\n * - `ARTIFACT_REF` blocks: delegates to the registered artifact component's `toClipboardText`\n * (e.g. the artifact title as plain text for table artifacts).\n * - Unknown block types: silently skipped (no registered service → no contribution to output).\n *\n * Falls back to `message.fallbackText` only when every block produces an empty string — this\n * guards against edge cases (empty message, all-unknown block types) while still preferring\n * the richer per-block text in the normal case.\n *\n * @param message The chat message whose blocks should be aggregated.\n * @returns A string suitable for writing to the system clipboard.\n */\nexport function copyMessageText(message: ChatMessage): string {\n const parts: string[] = [];\n\n for (const block of message.blocks) {\n const service = BlockServices[block.type];\n if (!service) continue;\n // `toClipboardText` is optional on the BlockService interface — external implementors may\n // omit it. Skip silently when absent (contributes \"\" to the aggregated output).\n if (!service.toClipboardText) continue;\n // The registry is keyed by BlockType and each service's toClipboardText is typed to its\n // concrete TBlock. The cast is safe because BlockServices[block.type] is always the matching\n // service for block.type — same soundness argument as the registry's own type erasure.\n const text = (service as { toClipboardText: (b: typeof block) => string }).toClipboardText(block);\n if (text) parts.push(text);\n }\n\n if (parts.length === 0) return message.fallbackText;\n return parts.join(\"\\n\\n\");\n}\n","import { useCallback } from \"react\";\nimport { IconCopy, IconCheck, IconThumbUp, IconThumbDown } from \"@tabler/icons-react\";\n\nimport { IconButton } from \"../../icon-button/icon-button\";\nimport { useChatContext } from \"../chat-context/chat-context\";\nimport { useCopyToClipboard } from \"../hooks/use-copy-to-clipboard\";\nimport { MESSAGE_ROLE } from \"../constants\";\nimport type { MessageRole } from \"../types\";\nimport { ActionsContainer, AnimatedAction } from \"./chat-message-actions-styles\";\n\ninterface ChatMessageActionsProps {\n className?: string;\n messageId: string;\n content: string;\n role: MessageRole;\n isHelpful?: boolean | null;\n}\n\nexport function ChatMessageActions({ className, messageId, content, role, isHelpful }: ChatMessageActionsProps) {\n const { onCopyMessage, onThumbUpClick, onThumbDownClick } = useChatContext();\n const { isCopied, copy } = useCopyToClipboard();\n\n const handleCopy = useCallback(() => {\n copy(content);\n onCopyMessage?.(messageId);\n }, [content, messageId, copy, onCopyMessage]);\n\n const handleThumbUp = useCallback(() => {\n onThumbUpClick?.(messageId, isHelpful === true ? null : true);\n }, [messageId, isHelpful, onThumbUpClick]);\n\n const handleThumbDown = useCallback(() => {\n onThumbDownClick?.(messageId, isHelpful === false ? null : false);\n }, [messageId, isHelpful, onThumbDownClick]);\n\n const isAssistant = role === MESSAGE_ROLE.ASSISTANT;\n const hasFeedback = isAssistant && (onThumbUpClick || onThumbDownClick);\n\n return (\n <ActionsContainer $role={role} className={className}>\n <IconButton\n icon={isCopied ? IconCheck : IconCopy}\n onClick={handleCopy}\n aria-label={isCopied ? \"Copied\" : \"Copy message\"}\n />\n\n {hasFeedback && (\n <AnimatedAction $visible={isHelpful !== false}>\n <IconButton\n icon={IconThumbUp}\n onClick={handleThumbUp}\n isSelected={isHelpful === true}\n aria-label=\"Good response\"\n />\n </AnimatedAction>\n )}\n\n {hasFeedback && (\n <AnimatedAction $visible={isHelpful !== true}>\n <IconButton\n icon={IconThumbDown}\n onClick={handleThumbDown}\n isSelected={isHelpful === false}\n aria-label=\"Bad response\"\n />\n </AnimatedAction>\n )}\n </ActionsContainer>\n );\n}\n","import { createContext, useContext } from \"react\";\nimport type { ChatContextValue } from \"../types\";\n\nexport const ChatContext = createContext<ChatContextValue | null>(null);\n\nexport function useChatContext(): ChatContextValue {\n const context = useContext(ChatContext);\n if (!context) {\n throw new Error(\"useChatContext must be used within a Chat.Root component\");\n }\n return context;\n}\n","import { useState, useCallback, useRef, useEffect } from \"react\";\n\nimport { copyToClipboard } from \"@/utils/clipboard\";\n\nconst RESET_DELAY_MS = 2000;\n\ninterface UseCopyToClipboardResult {\n isCopied: boolean;\n copy: (text: string) => void;\n}\n\nexport function useCopyToClipboard(): UseCopyToClipboardResult {\n const [isCopied, setIsCopied] = useState(false);\n const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n useEffect(() => {\n return () => {\n if (timeoutRef.current) clearTimeout(timeoutRef.current);\n };\n }, []);\n\n // Write text verbatim — callers (copyMessageText + each BlockService.toClipboardText) are\n // responsible for converting their own format to clipboard-ready plain text.\n const copy = useCallback((text: string) => {\n copyToClipboard(text, {\n onSuccess: () => {\n setIsCopied(true);\n if (timeoutRef.current) clearTimeout(timeoutRef.current);\n timeoutRef.current = setTimeout(() => {\n setIsCopied(false);\n timeoutRef.current = null;\n }, RESET_DELAY_MS);\n },\n });\n }, []);\n\n return { isCopied, copy };\n}\n","/** Options for the {@link copyToClipboard} function. */\ninterface CopyToClipboardOptions {\n /** Called after the text has been successfully written to the clipboard. */\n onSuccess?: () => void;\n /** Called if the clipboard write fails (e.g., permission denied). */\n onFailure?: (error: unknown) => void;\n}\n\n/**\n * Copies text to the system clipboard using the Clipboard API.\n * @param text - The string to copy.\n * @param options - Optional success/failure callbacks.\n */\nexport function copyToClipboard(text: string, options?: CopyToClipboardOptions): void {\n navigator.clipboard.writeText(text).then(\n () => options?.onSuccess?.(),\n (error) => options?.onFailure?.(error)\n );\n}\n","export const MESSAGE_ROLE = {\n USER: \"user\",\n ASSISTANT: \"assistant\",\n} as const;\n","import styled from \"styled-components\";\n\nimport { MESSAGE_ROLE } from \"../constants\";\nimport type { MessageRole } from \"../types\";\n\nconst FADE_DURATION_MS = 150;\nconst ANIMATE_DURATION_MS = 200;\n\nexport const ActionsContainer = styled.div<{ $role: MessageRole }>`\n display: flex;\n opacity: ${({ $role }) => ($role === MESSAGE_ROLE.ASSISTANT ? 1 : 0)};\n pointer-events: ${({ $role }) => ($role === MESSAGE_ROLE.ASSISTANT ? \"auto\" : \"none\")};\n transition: opacity ${FADE_DURATION_MS}ms ease;\n`;\n\nexport const AnimatedAction = styled.div<{ $visible: boolean }>`\n display: flex;\n overflow: hidden;\n max-width: ${({ $visible }) => ($visible ? \"40px\" : \"0\")};\n opacity: ${({ $visible }) => ($visible ? 1 : 0)};\n transition: max-width ${ANIMATE_DURATION_MS}ms ease, opacity ${ANIMATE_DURATION_MS}ms ease;\n`;\n","import styled from \"styled-components\";\n\nimport { colors } from \"../../../tokens/colors\";\nimport { typographyMixin, typographyTypes } from \"../../../tokens/typography\";\nimport { ActionsContainer } from \"../chat-message-actions/chat-message-actions-styles\";\nimport { MESSAGE_ROLE } from \"../constants\";\nimport type { MessageRole } from \"../types\";\n\nexport const MessageRow = styled.div<{ $role: MessageRole }>`\n display: flex;\n justify-content: ${({ $role }) => ($role === MESSAGE_ROLE.USER ? \"flex-end\" : \"flex-start\")};\n\n &:hover ${ActionsContainer} {\n opacity: 1;\n pointer-events: auto;\n }\n`;\n\nexport const MessageContainer = styled.div<{ $role: MessageRole }>`\n display: flex;\n flex-direction: column;\n align-items: ${({ $role }) => ($role === MESSAGE_ROLE.USER ? \"flex-end\" : \"flex-start\")};\n max-width: 90%;\n width: ${({ $role }) => ($role === MESSAGE_ROLE.USER ? \"auto\" : \"100%\")};\n gap: 12px;\n`;\n\nexport const MessageBubble = styled.div<{ $role: MessageRole }>`\n ${typographyMixin(typographyTypes.GEIST_BODY_S_REGULAR)};\n color: ${colors[\"brown-100\"]};\n background: ${({ $role }) => ($role === MESSAGE_ROLE.USER ? colors.white : \"transparent\")};\n padding: ${({ $role }) => ($role === MESSAGE_ROLE.USER ? \"12px 16px\" : \"0\")};\n border-radius: ${({ $role }) => ($role === MESSAGE_ROLE.USER ? \"16px\" : \"0\")};\n max-width: 100%;\n width: 100%;\n min-width: 0;\n /* Vertical rhythm between consecutive blocks — renderer-owned, not agent-emitted.\n * Industry standard (Claude, ChatGPT, Vercel AI Elements): the chat renderer applies\n * spacing via CSS; agents emit semantic blocks only. Value matches the MessageContainer\n * gap. TODO: replace 12px with a clay spacing token when a spacing scale is added. */\n display: flex;\n flex-direction: column;\n gap: 12px;\n`;\n"],"mappings":"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","names":["import_react","import_react","__iconNode","__iconNode","__iconNode","__iconNode","__iconNode","__iconNode","__iconNode","__iconNode","import_react","import_react","import_react","styled","import_jsx_runtime","Icon","import_styled_components","styled","import_jsx_runtime","IconButton","import_jsx_runtime","import_react","copyNodeAsPng","import_jsx_runtime","MAX_ERROR_MESSAGE_CHARS","truncateErrorMessage","import_jsx_runtime","DEFAULT_LABEL","import_react","React","import_jsx_runtime","import_jsx_runtime","downloadNodeAsPng","import_react","import_react","import_recharts","import_jsx_runtime","import_jsx_runtime","import_jsx_runtime","import_jsx_runtime","RechartsBarChart","import_jsx_runtime","import_react","import_recharts","import_jsx_runtime","RechartsLineChart","import_jsx_runtime","import_react","import_recharts","import_jsx_runtime","RechartsPieChart","import_jsx_runtime","import_jsx_runtime","Papa","import_react","import_jsx_runtime","import_react","React","import_jsx_runtime","import_react","import_jsx_runtime","import_jsx_runtime","TITLE_CLASSES","import_jsx_runtime","import_styled_components","styled","import_jsx_runtime","import_styled_components","styled","import_jsx_runtime","ReactMarkdown","remarkGfm","remarkBreaks","import_jsx_runtime","import_react","import_react","import_react","import_styled_components","styled","import_jsx_runtime","import_styled_components","styled","import_jsx_runtime"]}
1
+ {"version":3,"sources":["../src/capture/capture-exclude-attribute.ts","../src/tokens/colors.ts","../src/capture/_font-data-urls.generated.ts","../src/capture/font-embed.ts","../src/capture/dom-to-image.ts","../src/components/chat/index.ts","../src/components/chat/chat-message/chat-message.tsx","../src/components/blocks/types.ts","../src/components/artifacts/artifact-base.ts","../src/components/artifacts/chart-artifact/types.ts","../src/components/artifacts/table-artifact/types.ts","../src/components/artifacts/chart-artifact/chart-artifact.tsx","../src/components/ai-elements/artifact.tsx","../src/lib/utils.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/IconCheck.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/icon-button/icon-button.tsx","../src/components/icon/icon.tsx","../src/components/icon/icon-styles.ts","../src/components/icon-button/icon-button-styles.ts","../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/status-message.tsx","../src/components/chart/bar-chart/bar-chart.tsx","../src/components/chart/chart-container.tsx","../src/components/chart/chart-legend-content.tsx","../src/components/chart/chart-tooltip-content.tsx","../src/tokens/index.ts","../src/tokens/typography.ts","../src/components/chart/chart-palette.ts","../src/components/chart/chart-styles.ts","../src/components/chart/chart-tick-utils.ts","../src/components/chart/coerce-numeric.ts","../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/chart/line-chart/line-chart.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/chart/pie-chart/pie-chart.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/chart-artifact/to-clipboard-text.ts","../src/components/artifacts/artifact-services/chart-artifact-service.ts","../src/components/artifacts/table-artifact/table-artifact.tsx","../src/components/ui/table.tsx","../src/components/table/use-scroll-shadow.ts","../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/table-artifact/use-table-artifact-data.ts","../src/components/artifacts/table-artifact/validate.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","../src/components/blocks/artifact-ref-block/artifact-ref-block.tsx","../src/components/blocks/block-services/artifact-ref-block-service.ts","../src/utils/markdown-to-plain-text.ts","../src/components/blocks/text-block/text-block.tsx","../src/components/blocks/scrollable-table/scrollable-table-styles.ts","../src/components/blocks/scrollable-table/scrollable-table.tsx","../src/components/blocks/text-block/sanitize.ts","../src/components/blocks/text-block/text-block-styles.ts","../src/components/blocks/block-services/text-block-service.ts","../src/components/blocks/block-services/index.ts","../src/components/blocks/block/block.tsx","../src/components/chat/hooks/copy-message-text.ts","../src/components/chat/chat-message-actions/chat-message-actions.tsx","../src/components/chat/chat-context/chat-context.ts","../src/components/chat/hooks/use-copy-to-clipboard.ts","../src/utils/clipboard.ts","../src/components/chat/constants.ts","../src/components/chat/chat-message-actions/chat-message-actions-styles.ts","../src/components/chat/chat-message/chat-message-styles.ts"],"sourcesContent":["/**\n * Marker attribute the default filter uses to exclude subtrees from a DOM-to-image capture.\n * Apply it to elements that should not appear in the rendered image — e.g. action toolbars\n * that would otherwise capture themselves.\n *\n * **Important**: do not apply this attribute to the capture root itself. The default filter\n * runs on every descendant *including* the root; marking the root excludes the entire subtree\n * and `toBlob()` returns `null`, surfacing as a \"Copy failed\" state in the UI. Consumers should\n * apply it to a child (typically the actions toolbar), never to the same node referenced by\n * `targetRef`.\n *\n * Lives in its own module so consumers like `chart-artifact.tsx` can import the string\n * constant without dragging in the heavy `dom-to-image.ts` module (font data URLs +\n * html-to-image). Re-exported from both `dom-to-image.ts` (for the default filter to read)\n * and `capture/index.ts` (for external consumers).\n */\nexport const CAPTURE_EXCLUDE_ATTRIBUTE = \"data-clay-capture-exclude\";\n","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","/**\n * AUTO-GENERATED — DO NOT EDIT BY HAND.\n *\n * Run `bun run gen:font` (also runs automatically before `bun run build`) to refresh from the\n * `@fontsource-variable/*` packages' woff2 binaries. See `scripts/generate-font-embed.ts` for\n * why this exists as a pre-build step rather than a tsup loader.\n */\n\nexport const GEIST_LATIN_WOFF2_DATA_URL: string =\n \"data:font/woff2;base64,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\";\n\nexport const CRIMSON_PRO_LATIN_WOFF2_DATA_URL: string =\n \"data:font/woff2;base64,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\";\n","// Data URLs are generated at build time by `scripts/generate-font-embed.ts` (auto-run via\n// `bun run gen:font` before every `bun run build`). Pre-computing as a `.ts` file sidesteps\n// tsup's `.woff2` loader entirely — esbuild's `noExternal` re-bundling does not apply asset\n// loaders to imports inside re-bundled deps, which leaves bare `.woff2` imports in the output\n// and crashes downstream bundlers. See `scripts/generate-font-embed.ts` for the full story.\nimport { CRIMSON_PRO_LATIN_WOFF2_DATA_URL, GEIST_LATIN_WOFF2_DATA_URL } from \"./_font-data-urls.generated\";\n\n/**\n * `@font-face` CSS for clay's two type-system fonts — Geist (primary sans) and Crimson Pro\n * (secondary serif). Both binaries are bundled as base64 data URLs — no runtime fetch required.\n *\n * Why bundle both: `html-to-image` rasterises through an SVG `<foreignObject>`, which renders\n * in a separate document context from the page. Fonts loaded in the page (e.g. via a Google\n * Fonts `<link>`) are NOT visible to the foreignObject — only fonts whose `@font-face` rule is\n * inlined in the captured CSS are. `getFontEmbedCSS` tries to inline them by reading\n * `document.styleSheets`, but cross-origin stylesheets (Google Fonts, any CDN) throw\n * `SecurityError` on `cssRules` access. The fallback fetch is then blocked by CORS preflight.\n * So we ship both with clay and skip the runtime-resolution mess entirely. Any chart artifact\n * that uses either family captures correctly; consumers don't need to mirror the font setup.\n *\n * `format(\"woff2-variations\")` is the correct format hint for a variable axis font — browsers\n * supporting variable fonts pick this up; older browsers ignore the rule and fall back to the\n * next entry in the font stack.\n *\n * Subset is `latin` only for both (~29 KB Geist + ~47 KB Crimson Pro on disk → ~40 KB + ~63 KB\n * base64). Latin covers all chat-UI English content; consumers that need Hebrew / Cyrillic /\n * CJK PNG capture pass their own `fontEmbedCSS` via `NodeToImageOptions.fontEmbedCSS`.\n */\nexport const CLAY_FONTS_EMBED_CSS = `\n@font-face {\n font-family: 'Geist';\n font-style: normal;\n font-weight: 100 900;\n font-display: swap;\n src: url(${GEIST_LATIN_WOFF2_DATA_URL}) format('woff2-variations');\n}\n@font-face {\n font-family: 'Crimson Pro';\n font-style: normal;\n font-weight: 200 900;\n font-display: swap;\n src: url(${CRIMSON_PRO_LATIN_WOFF2_DATA_URL}) format('woff2-variations');\n}\n`.trim();\n","import { getFontEmbedCSS, toBlob } from \"html-to-image\";\n\nimport { colors } from \"@/tokens/colors\";\n\nimport { CAPTURE_EXCLUDE_ATTRIBUTE } from \"./capture-exclude-attribute\";\nimport { CLAY_FONTS_EMBED_CSS } from \"./font-embed\";\n\n// Re-export so external consumers of `@shapesos/clay/capture` keep working unchanged. The\n// canonical definition lives in `./capture-exclude-attribute` so consumers like\n// `chart-artifact.tsx` can import the string without pulling in this heavy module.\nexport { CAPTURE_EXCLUDE_ATTRIBUTE };\n\n/** Options for DOM-to-image conversions. Forwarded to `html-to-image`'s `toBlob`. */\nexport interface NodeToImageOptions {\n /**\n * Background color filled before rasterising. Default: clay `white` token (`#FFFFFF`).\n * A solid background prevents the PNG from looking broken when pasted onto light or dark\n * surfaces (Slack, Notion, VS Code, etc.) — transparent PNGs render unpredictably.\n */\n backgroundColor?: string;\n /**\n * Device pixel ratio for retina-sharp output. Default: `window.devicePixelRatio || 1`.\n *\n * Memory implication: each copy allocates a canvas backing-store sized to\n * `width × height × pixelRatio²`. On a 3× HiDPI display a 480px-wide chart card produces a\n * ~1440px PNG (~8 MB allocation per copy). Single-click use is fine; batch-capture flows\n * (\"copy every chart in this report\") should pass `pixelRatio: 1` to avoid stacking spikes.\n */\n pixelRatio?: number;\n /**\n * Predicate run against every descendant element. Returning `false` excludes the node and\n * its subtree from the image. The clay default excludes any element marked with\n * `data-clay-capture-exclude` — caller-supplied filters compose with (not replace) the\n * default: a node is included only when both the default and the caller filter accept it.\n */\n filter?: (node: HTMLElement) => boolean;\n /** Override the captured width in pixels. Default: `node.offsetWidth`. */\n width?: number;\n /** Override the captured height in pixels. Default: `node.offsetHeight`. */\n height?: number;\n /**\n * Force-reload `<img>` sources to defeat stale-cache rendering. Default: `true`.\n * Note that this only cache-busts `<img>` `src` resources — fonts and CSS-referenced URLs\n * (e.g. `background-image: url(...)`) go through `html-to-image`'s separate global URL cache\n * which does NOT honor this flag.\n */\n cacheBust?: boolean;\n /**\n * Pre-resolved `@font-face` CSS so embedded text uses the correct fonts. When omitted, this\n * module resolves the document's font-embed CSS lazily on the first capture and reuses the\n * cached value thereafter — avoiding a recurring main-thread block to re-walk\n * `document.styleSheets` and re-base64-encode webfont files on every copy.\n */\n fontEmbedCSS?: string;\n}\n\nconst DEFAULT_BACKGROUND = colors.white;\n\n/** Truncation cap for error messages surfaced in UI tooltips. Long S3 presigned URLs or\n * stringified stack frames from `html-to-image`'s internals can otherwise leak verbatim. */\nconst MAX_ERROR_MESSAGE_CHARS = 120;\n\nfunction defaultFilter(node: HTMLElement): boolean {\n if (typeof node.getAttribute !== \"function\") return true;\n return !node.hasAttribute(CAPTURE_EXCLUDE_ATTRIBUTE);\n}\n\nfunction composeFilter(caller: NodeToImageOptions[\"filter\"]): (node: HTMLElement) => boolean {\n if (!caller) return defaultFilter;\n return (node) => defaultFilter(node) && caller(node);\n}\n\n/**\n * Lazy singleton for the document's resolved `@font-face` embed CSS.\n *\n * The result is always **prefixed with clay's bundled Geist CSS** (see `font-embed.ts`), then\n * appended with whatever `html-to-image`'s `getFontEmbedCSS` can extract from\n * `document.styleSheets`. The bundled prefix guarantees Geist renders in the captured PNG\n * even when the consumer page loads Geist from a CORS-walled source (Google Fonts is the\n * common case — its CSS is unreadable from JS so `getFontEmbedCSS` returns an empty string\n * and the foreignObject falls back to a system font, which renders wider than Geist and makes\n * the captured layout reflow at apparent narrower widths). Anything else\n * `html-to-image` discovers (consumer's own webfonts, icon fonts, etc.) is layered on top.\n *\n * Computed once per session and cached — avoids repeated `document.styleSheets` traversals\n * and webfont URL fetches that would each add 100–500 ms of main-thread work per copy.\n */\nlet cachedFontEmbedCSS: string | null = null;\nlet inflightFontEmbedCSS: Promise<string> | null = null;\n\nasync function resolveFontEmbedCSS(node: HTMLElement): Promise<string> {\n if (cachedFontEmbedCSS !== null) return cachedFontEmbedCSS;\n if (inflightFontEmbedCSS !== null) return inflightFontEmbedCSS;\n inflightFontEmbedCSS = getFontEmbedCSS(node)\n .catch(() => \"\")\n .then((dynamicCss) => {\n const combined = `${CLAY_FONTS_EMBED_CSS}\\n${dynamicCss}`;\n cachedFontEmbedCSS = combined;\n return combined;\n })\n .finally(() => {\n inflightFontEmbedCSS = null;\n });\n return inflightFontEmbedCSS;\n}\n\n/**\n * Test-only escape hatch to clear the module-level `cachedFontEmbedCSS` / `inflightFontEmbedCSS`\n * singletons. Vitest's default worker pool re-uses module instances across tests in the same\n * file; without an explicit reset, the first test populates the cache and every subsequent test\n * skips `getFontEmbedCSS` entirely — masking any per-test mocking the caller set up.\n *\n * Call in `beforeEach` of any test file that exercises `nodeToPngBlob` / `copyNodeAsPng` /\n * `downloadNodeAsPng` and depends on a fresh font-CSS resolution. Not exported from\n * `@shapesos/clay/capture` (the leading underscore marks it as internal).\n */\nexport function _resetFontEmbedCacheForTests(): void {\n cachedFontEmbedCSS = null;\n inflightFontEmbedCSS = null;\n}\n\n/**\n * Rasterise `node` to a PNG `Blob`. **Browser-only at call time**, but **import-safe under SSR**:\n * the module itself does not touch browser globals at module load, so a Node / SSR consumer can\n * `import { nodeToPngBlob } from \"@shapesos/clay/capture\"` without crashing. Calling any of the\n * three capture functions in a non-browser context throws — `nodeToPngBlob` references `<canvas>`\n * via `html-to-image`; `copyNodeAsPng` checks `ClipboardItem` / `navigator.clipboard` explicitly;\n * `downloadNodeAsPng` uses `URL.createObjectURL` + `document`. Guard at the call site with\n * `typeof window !== \"undefined\"` if you need to short-circuit SSR.\n *\n * Throws when `html-to-image` reports a null blob (browser couldn't rasterise) or when the\n * underlying conversion rejects.\n *\n * **Caveats** (worth knowing before adopting in a new artifact type):\n * - **Cross-origin tainting**: if the captured subtree contains a cross-origin `<img>`,\n * `<image href>` SVG element, or any other resource the browser flags as cross-origin, the\n * canvas is tainted and `toBlob()` returns `null` — surfaced as a \"Failed to rasterise\" error.\n * This is browser-enforced; no clay-side fix exists. Ensure user-controlled annotations in\n * chart subtrees don't reference cross-origin resources.\n * - **Global URL cache in the underlying library**: `html-to-image` maintains a module-global\n * cache of image-src → base64. The cache is per-tab and lives for the SPA session. Today\n * clay's only consumer (chart artifacts) renders SVG, so the cache stays empty in practice —\n * but future artifact types that embed user-uploaded `<img>` content should be aware that\n * image URLs are cached by URL, not by user identity.\n */\nexport async function nodeToPngBlob(node: HTMLElement, options: NodeToImageOptions = {}): Promise<Blob> {\n const fontEmbedCSS = options.fontEmbedCSS ?? (await resolveFontEmbedCSS(node).catch(() => undefined));\n // Default width/height from the node's actual rendered dimensions. Without explicit values,\n // `html-to-image` uses `node.offsetWidth` for the *canvas* size but does NOT pin the cloned\n // root's CSS width — so when the clone is placed offscreen for serialisation, `w-full` and\n // other percentage-width descendants re-evaluate against whatever the offscreen container's\n // effective width is, causing HTML elements (titles, legends, anything not yet a rasterised\n // SVG attribute) to reflow at a narrower width than the live element. Passing explicit\n // width/height applies them as inline styles on the cloned root, locking the layout context.\n // `getBoundingClientRect()` is subpixel-accurate where `offsetWidth` rounds to integers —\n // matters for layouts that wrap at sub-pixel boundaries (Tailwind's `min-w-[…]` constraints\n // commonly land here).\n const rect = node.getBoundingClientRect();\n const width = options.width ?? rect.width;\n const height = options.height ?? rect.height;\n const blob = await toBlob(node, {\n backgroundColor: options.backgroundColor ?? DEFAULT_BACKGROUND,\n cacheBust: options.cacheBust ?? true,\n filter: composeFilter(options.filter),\n pixelRatio: options.pixelRatio,\n width,\n height,\n fontEmbedCSS,\n });\n if (!blob) throw new Error(\"Failed to rasterise node — toBlob returned null (possible cross-origin canvas taint).\");\n return blob;\n}\n\n/**\n * Rasterise `node` and write the resulting PNG to the system clipboard.\n *\n * Must be called from within a user gesture (button click, keypress) — browsers reject\n * clipboard writes from non-gesture contexts. The in-flight `Promise<Blob>` is passed\n * directly into `ClipboardItem` rather than awaited first, so Safari's \"Blob must be\n * created during the user gesture\" rule is satisfied.\n *\n * Throws when the clipboard API is unavailable (non-secure contexts: HTTP, certain embedded\n * frames), when the rasterise fails, or when the user has revoked clipboard-write permission.\n */\nexport async function copyNodeAsPng(node: HTMLElement, options: NodeToImageOptions = {}): Promise<void> {\n if (typeof ClipboardItem === \"undefined\") {\n throw new Error(\"Clipboard image writes are not supported in this environment (ClipboardItem missing).\");\n }\n if (!navigator.clipboard) {\n throw new Error(\"Clipboard API unavailable — secure context (HTTPS) required.\");\n }\n\n const blobPromise = nodeToPngBlob(node, options);\n // Attach a no-op catch so the rejection doesn't surface as \"unhandled\" before the clipboard\n // write rejects with the same error. The original promise is still awaited below.\n void blobPromise.catch(() => undefined);\n\n const item = new ClipboardItem({ \"image/png\": blobPromise });\n // Browsers reject `write()` if the promise inside the ClipboardItem rejects. Run the write\n // and the underlying blob fetch in parallel — whichever surfaces first is the error.\n await Promise.all([navigator.clipboard.write([item]), blobPromise]);\n}\n\n/**\n * Window between `<a>.click()` and `URL.revokeObjectURL` for the object URL used by\n * `downloadNodeAsPng`. The click triggers the download asynchronously — Firefox in\n * particular reads the blob URL on a separate task and will fail silently if the URL is\n * revoked before that read completes. 30s is well past any browser's start-the-download\n * latency budget and small enough that even abandoned downloads don't hold the blob in\n * memory for more than the user's typical interactive session.\n */\nconst OBJECT_URL_REVOKE_DELAY_MS = 30_000;\n\n/**\n * Rasterise `node` and trigger a browser file download of the resulting PNG. Uses an\n * `<a download>` with an object URL — the URL is revoked on a deferred timer so it doesn't\n * leak into the document for the lifetime of the tab but also isn't revoked before the\n * browser reads it.\n *\n * Must be called from a user gesture (the `<a>.click()` only triggers a download from inside one).\n *\n * @param filename The suggested filename presented to the user (e.g. `\"chart-2026-05-25.png\"`).\n * The `.png` extension is NOT appended automatically — callers control the full\n * filename so they can include titles, dates, or artifact IDs.\n */\nexport async function downloadNodeAsPng(\n node: HTMLElement,\n filename: string,\n options: NodeToImageOptions = {}\n): Promise<void> {\n const blob = await nodeToPngBlob(node, options);\n const url = URL.createObjectURL(blob);\n const a = document.createElement(\"a\");\n a.href = url;\n a.download = filename;\n a.rel = \"noopener noreferrer\";\n // Anchors must be in the DOM for some browsers (Firefox) to honour programmatic clicks\n // that trigger downloads. Append → click → remove keeps the side-effect surface minimal.\n document.body.appendChild(a);\n a.click();\n a.remove();\n // Defer the revoke: `a.click()` initiates the download asynchronously and Firefox reads the\n // blob URL on a separate task. A synchronous revoke (in a `finally` block immediately after\n // click) tears down the URL before the browser starts the read → silent download failure.\n // See Cursor Bugbot report on PR #60 + html-to-image issue tracker. The setTimeout is fire-and-\n // forget; if the tab closes first, the URL is freed by the document teardown anyway.\n setTimeout(() => URL.revokeObjectURL(url), OBJECT_URL_REVOKE_DELAY_MS);\n}\n\n/**\n * Trim an error message to a UI-safe length. Verbose error sources (`html-to-image` internals,\n * fetch failures with full S3 presigned URLs) can otherwise produce multi-kilobyte strings that\n * blow out tooltips and leak unnecessary detail.\n */\nexport function truncateErrorMessage(message: string, max = MAX_ERROR_MESSAGE_CHARS): string {\n return message.length <= max ? message : `${message.slice(0, max - 1)}…`;\n}\n","export { ChatMessage } from \"./chat-message/chat-message\";\nexport { useChatContext, ChatContext } from \"./chat-context/chat-context\";\nexport { useCopyToClipboard } from \"./hooks/use-copy-to-clipboard\";\nexport { copyMessageText } from \"./hooks/copy-message-text\";\nexport { MESSAGE_ROLE } from \"./constants\";\nexport type { ChatMessage as ChatMessageType, MessageRole, ChatContextValue } from \"./types\";\n","import { useMemo } from \"react\";\n\nimport { Block } from \"@/components/blocks/block\";\nimport { copyMessageText } from \"../hooks/copy-message-text\";\nimport type { ChatMessage as ChatMessageType } from \"../types\";\nimport { ChatMessageActions } from \"../chat-message-actions/chat-message-actions\";\nimport { MessageRow, MessageBubble, MessageContainer } from \"./chat-message-styles\";\n\ninterface ChatMessageProps {\n message: ChatMessageType;\n}\n\nexport function ChatMessage({ message }: ChatMessageProps) {\n // Aggregate per-block clipboard text so the copy button produces richer output than the\n // server-supplied `fallbackText` (which is typically \"[table: Title]\" for artifact blocks).\n // `useMemo` keeps the string stable across re-renders that don't change the blocks array.\n const clipboardText = useMemo(() => copyMessageText(message), [message]);\n\n return (\n <MessageRow $role={message.role} role=\"article\" aria-label={`${message.role} message`}>\n <MessageContainer $role={message.role}>\n <MessageBubble $role={message.role} data-testid=\"message-bubble\">\n {message.blocks.map((block, index) => (\n <Block key={index} block={block} />\n ))}\n </MessageBubble>\n <ChatMessageActions\n messageId={message.id}\n content={clipboardText}\n role={message.role}\n isHelpful={message.isHelpful}\n />\n </MessageContainer>\n </MessageRow>\n );\n}\n","import type { ComponentType } from \"react\";\n\nimport type { ArtifactCallbacksOf, ArtifactLabelsOf, ArtifactRecord } from \"@/components/artifacts/types\";\n\n/** Block-type discriminator constants. Mirrors the wire shape from io-server / shapes-agent. */\nexport const blockTypes = {\n TEXT: \"TEXT\",\n ARTIFACT_REF: \"ARTIFACT_REF\",\n} as const;\n\n/** Discriminator for content blocks. */\nexport type BlockType = (typeof blockTypes)[keyof typeof blockTypes];\n\n/** Plain-text content block — rendered as Markdown. */\nexport interface TextBlockRecord {\n /** Block-type discriminator. */\n type: typeof blockTypes.TEXT;\n /** Type-specific data; kept as `payload` to match the wire shape 1:1. */\n payload: {\n /** The Markdown source for this text segment. */\n text: string;\n };\n}\n\n/**\n * Reference-to-an-artifact content block — dispatched to the per-type artifact component.\n *\n * The artifact-domain types (`ArtifactRecord`, `TableArtifactRecord`, etc.) live in\n * `@/components/artifacts/types` so the artifact layer can be consumed independently of the\n * block layer (e.g. by a future surface that renders an artifact outside chat). The block layer\n * imports them here to wrap an artifact in an `ARTIFACT_REF` block.\n *\n * `ArtifactRefBlockRecord` is a *distributed* union over `ArtifactRecord` branches — TS narrows\n * `labels` and `callbacks` to the slice matching `artifact.type` via `ArtifactLabelsOf<T>` /\n * `ArtifactCallbacksOf<T>`. Without this distribution, `Partial<ChartLabels | TableLabels>`\n * collapses to `Partial<ChartLabels> | Partial<TableLabels>` and TS accepts chart-only keys on\n * a TABLE block's payload (the discriminator becomes load-bearing only at runtime, not in the\n * type system).\n */\nexport type ArtifactRefBlockRecord = ArtifactRecord extends infer TArtifact\n ? TArtifact extends ArtifactRecord\n ? {\n /** Block-type discriminator. */\n type: typeof blockTypes.ARTIFACT_REF;\n /** Type-specific data; kept as `payload` to match the wire shape 1:1. */\n payload: {\n /** The artifact's stable identifier, used for keying / future cross-message reuse. */\n artifactId: string;\n /** Inlined artifact metadata — clients fetch the underlying data via `artifact.protectedAsset.presignedUrl`. */\n artifact: TArtifact;\n /**\n * Optional consumer-supplied labels for the rendered artifact's action buttons + status\n * messages — a flat slice narrowed by `artifact.type`. NOT a wire field; the consumer\n * constructs this from their i18n layer when materialising the block-record from the\n * server payload, then passes the augmented block to `<Block>`. The dispatcher forwards\n * the slice directly to the matching renderer — no per-type key wrapping (the block\n * already carries the discriminator).\n *\n * TS catches cross-type assignments here: `block.payload.labels = { downloadCsv: 'X' }`\n * on a `TableArtifactRecord`-typed block is a type error because `downloadCsv` isn't a\n * `TableArtifactLabels` key.\n */\n labels?: Partial<ArtifactLabelsOf<TArtifact>>;\n /**\n * Optional side-effect callbacks for actions on the rendered artifact (e.g. `onDownload`).\n * Flat slice narrowed by `artifact.type` via `ArtifactCallbacksOf<T>`. NOT a wire field\n * — the consumer wires this once to bridge artifact actions to their analytics /\n * observability layer.\n */\n callbacks?: Partial<ArtifactCallbacksOf<TArtifact>>;\n };\n }\n : never\n : never;\n\n/** Discriminated union of all supported content blocks. Additive — new types extend this union. */\nexport type BlockRecord = TextBlockRecord | ArtifactRefBlockRecord;\n\n/** Props every concrete block component receives from the dispatcher. */\nexport interface BlockComponentProps<TBlock extends BlockRecord = BlockRecord> {\n /** The block to render. */\n block: TBlock;\n}\n\n/** Per-block-type service — registry entry that pairs a discriminator with its concrete component. */\nexport interface BlockService<TBlock extends BlockRecord = BlockRecord> {\n /** Discriminator value this service handles. */\n type: BlockType;\n /** React component that renders a block of this type. */\n Component: ComponentType<BlockComponentProps<TBlock>>;\n /**\n * Returns a plain-text or markdown string representation of this block suitable for clipboard\n * copy. Called by `copyMessageText` when the user clicks \"copy message\".\n *\n * - `TextBlockService`: returns `block.payload.text` verbatim (markdown preserved).\n * - `ArtifactRefBlockService`: delegates to `ArtifactServices[artifact.type].toClipboardText`.\n * Falls back to `[Artifact: <title>]` when the artifact type has no registered component.\n *\n * Optional so external `BlockService` implementors don't break on upgrade — when omitted, the\n * aggregator treats the block as contributing the empty string (silently skipped). Implementations\n * must be pure, synchronous functions — no async, no hooks, no side effects.\n */\n toClipboardText?: (block: TBlock) => string;\n}\n","/**\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} 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 { 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 { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\n/**\n * Combines class names using clsx and tailwind-merge — the standard shadcn/ui helper.\n * Tailwind classes that conflict (e.g. `p-2 p-4`) get merged so the last one wins.\n */\nexport function cn(...inputs: ClassValue[]): string {\n return twMerge(clsx(inputs));\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\":\"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\":\"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 { forwardRef } from \"react\";\n\nimport { Icon } from \"../icon/icon\";\nimport { Button } from \"./icon-button-styles\";\nimport type { IconButtonProps, IconButtonSize } from \"./types\";\n\nconst ICON_SIZE_BY_BUTTON_SIZE: Record<IconButtonSize, number> = {\n small: 16,\n medium: 20,\n};\n\n/** Ghost-style icon button. `forwardRef` + the rest-props spread so Radix `Slot`-based wrappers\n * (Tooltip, Popover, DropdownMenu) can attach refs and event handlers via `asChild`. */\nexport const IconButton = forwardRef<HTMLButtonElement, IconButtonProps>(function IconButton(\n {\n icon,\n size = \"small\",\n variant = \"short\",\n isSelected = false,\n disabled = false,\n onClick,\n className,\n id,\n as,\n \"aria-label\": ariaLabel,\n ...rest\n },\n ref\n) {\n // Only set `type=\"button\"` when rendering as a native button. Anchors (`as=\"a\"`) treat the\n // `type` attribute as a content-type hint for the linked resource — `type=\"button\"` on an\n // anchor is semantically wrong and shows up as an a11y / HTML-validation noise warning.\n const isNativeButton = as === undefined || as === \"button\";\n return (\n <Button\n ref={ref}\n className={className}\n id={id}\n as={as}\n $size={size}\n $variant={variant}\n $isSelected={isSelected}\n disabled={disabled}\n onClick={disabled ? undefined : onClick}\n aria-label={ariaLabel}\n {...(isNativeButton ? { type: \"button\" as const } : {})}\n {...rest}\n >\n <Icon icon={icon} size={ICON_SIZE_BY_BUTTON_SIZE[size]} />\n </Button>\n );\n});\n","import { forwardRef } from \"react\";\n\nimport { IconWrapper, getStrokeWidth } from \"./icon-styles\";\nimport type { IconProps } from \"./types\";\n\nconst DEFAULT_SIZE = 16;\n\nexport const Icon = forwardRef<HTMLSpanElement, IconProps>(function Icon(\n { icon: IconComponent, size = DEFAULT_SIZE, color, className, \"aria-label\": ariaLabel },\n ref\n) {\n return (\n <IconWrapper\n ref={ref}\n className={className}\n $color={color}\n aria-label={ariaLabel}\n role={ariaLabel ? \"img\" : undefined}\n >\n <IconComponent width={size} height={size} strokeWidth={getStrokeWidth(size)} />\n </IconWrapper>\n );\n});\n","import styled from \"styled-components\";\n\nexport const STROKE_WIDTH_BY_SIZE: Record<number, number> = {\n 12: 1.4,\n 14: 1.4,\n 16: 1.8,\n 18: 1.8,\n 20: 1.8,\n 24: 1.8,\n};\n\nexport const DEFAULT_STROKE_WIDTH = 1.8;\n\nexport function getStrokeWidth(size: number): number {\n return STROKE_WIDTH_BY_SIZE[size] ?? DEFAULT_STROKE_WIDTH;\n}\n\nexport const IconWrapper = styled.span<{ $color?: string }>`\n display: inline-flex;\n align-items: center;\n justify-content: center;\n color: ${({ $color }) => $color ?? \"currentColor\"};\n flex-shrink: 0;\n`;\n","import styled from \"styled-components\";\n\nimport { colors } from \"../../tokens/colors\";\nimport type { IconButtonSize, IconButtonVariant } from \"./types\";\n\nconst PADDING: Record<IconButtonVariant, Record<IconButtonSize, string>> = {\n short: {\n small: \"4px\",\n medium: \"6px\",\n },\n long: {\n small: \"4px 6px\",\n medium: \"6px 10px\",\n },\n};\n\nconst BORDER_RADIUS: Record<IconButtonSize, number> = {\n small: 6,\n medium: 10,\n};\n\nexport const Button = styled.button<{\n $size: IconButtonSize;\n $variant: IconButtonVariant;\n $isSelected: boolean;\n}>`\n display: inline-flex;\n align-items: center;\n justify-content: center;\n padding: ${({ $size, $variant }) => PADDING[$variant][$size]};\n border: none;\n border-radius: ${({ $size }) => BORDER_RADIUS[$size]}px;\n background: ${({ $isSelected }) => ($isSelected ? colors[\"brown-40\"] : \"transparent\")};\n color: ${colors[\"brown-100\"]};\n cursor: pointer;\n transition: background-color 100ms ease;\n\n &:hover {\n background: ${({ $isSelected }) => ($isSelected ? colors[\"brown-40\"] : colors[\"brown-20\"])};\n }\n\n &:active {\n background: ${colors[\"brown-40\"]};\n }\n\n &:disabled {\n color: ${colors[\"brown-50\"]};\n background: transparent;\n cursor: not-allowed;\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 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","import { type ReactNode, useMemo } from \"react\";\nimport { Bar, BarChart as RechartsBarChart, CartesianGrid, LabelList, Legend, Tooltip, XAxis, YAxis } from \"recharts\";\n\nimport { ChartContainer } from \"../chart-container\";\nimport { ChartLegendContent } from \"../chart-legend-content\";\nimport { ChartTooltipContent } from \"../chart-tooltip-content\";\nimport { CHART_PALETTE, colorForSeriesIndex } from \"../chart-palette\";\nimport {\n BAR_TOOLTIP_CURSOR_FILL,\n CHART_AXIS_PROPS,\n CHART_GRID_PROPS,\n CHART_MARGIN,\n CHART_VALUE_LABEL_STYLE,\n} from \"../chart-styles\";\nimport { defaultBarTickFormatter } from \"../chart-tick-utils\";\nimport { coerceNumericColumns } from \"../coerce-numeric\";\nimport type { ChartSeries } from \"../types\";\n\nexport interface BarChartProps {\n /** Rows to plot. Each row should carry the `xKey` column and one column per `series[].key`. */\n data: ReadonlyArray<Record<string, unknown>>;\n /** CSV/data column to use for the categorical x-axis (e.g. `\"month\"`, `\"quarter\"`). */\n xKey: string;\n /** One descriptor per bar series. Length 1 = single-series chart; 2+ = grouped or stacked. */\n series: ChartSeries[];\n /**\n * When true, all series stack on one stack-id (vertical sum). When false (default), bars\n * group side-by-side per x-tick.\n */\n stacked?: boolean;\n /** Container height. Number = px, string = any CSS length. Default 320. */\n height?: number | string;\n /** Container width. Default `\"100%\"`. */\n width?: number | string;\n /** Optional className on the outer container. */\n className?: string;\n /** Override the default palette (`CHART_PALETTE`). Colors are assigned by series index. */\n palette?: readonly string[];\n /** Show the tooltip on hover. Default true. */\n showTooltip?: boolean;\n /**\n * Show the legend at the top. **Default behavior**: auto-hidden for single-series charts\n * (the legend would just label the one and only color, which is noise). Pass `true` to force\n * the legend even with one series; pass `false` to suppress for multi-series.\n */\n showLegend?: boolean;\n /** Show horizontal dashed grid lines. Default true. */\n showGrid?: boolean;\n /** Show the x-axis. Default true. */\n showXAxis?: boolean;\n /** Show the y-axis. Default true. */\n showYAxis?: boolean;\n /**\n * Show numeric value labels above each bar. **Default behavior**: auto-on for single\n * non-stacked series (labels stay readable); auto-off for stacked or multi-series bars\n * (labels overlap and clutter). Pass explicitly to override.\n */\n showValueLabels?: boolean;\n /**\n * Recharts XAxis tick `interval`. Default `0` — every category gets a label (no info loss\n * from auto-skipping). Use `\"preserveStartEnd\"` to let recharts drop colliding labels but\n * keep the first and last. For long category names, prefer `xAxisAngle` to rotate the labels\n * rather than dropping them.\n */\n xAxisInterval?: number | \"preserveStart\" | \"preserveEnd\" | \"preserveStartEnd\";\n /**\n * Rotate x-axis tick labels by this many degrees. **Default**: auto — `0` (horizontal) for\n * ≤7 categories; `-30` for 8+ categories so longer labels fit diagonally without overlapping\n * their neighbours. Pass `0` explicitly to force horizontal labels even at high counts; pass\n * any other number to set a custom angle.\n *\n * When the resolved angle is non-zero the chart auto-bumps its bottom margin and right-\n * anchors the labels so they don't run off the axis. The container `height` budget should\n * accommodate the angled labels (~40-60px more).\n */\n xAxisAngle?: number;\n /**\n * Custom formatter for x-axis tick labels. **Default**: clay auto-truncates labels based on\n * the number of categories so no labels overlap (see `getDefaultTickFormatter`). Pass an\n * explicit formatter to override — e.g. for word-boundary truncation, locale-aware\n * abbreviations, or to opt out of truncation entirely with `(v) => String(v)`.\n *\n * The tooltip continues to show the full unformatted value on hover regardless.\n */\n xAxisTickFormatter?: (value: unknown) => string;\n}\n\n/**\n * Pre-composed bar chart. Single-call usage for the common case; for custom recharts trees\n * compose `<ChartContainer>` + recharts primitives directly.\n *\n * ```tsx\n * <BarChart\n * data={[{ month: \"Jan\", us: 120, uk: 60 }, { month: \"Feb\", us: 132, uk: 64 }]}\n * xKey=\"month\"\n * series={[\n * { key: \"us\", label: \"US\" },\n * { key: \"uk\", label: \"UK\" },\n * ]}\n * stacked\n * />\n * ```\n *\n * **Smart defaults**: legend auto-hides for single-series charts; value labels auto-show for\n * single non-stacked series; all x-axis ticks are shown by default (no recharts auto-skip).\n * Override any of these via the named props.\n *\n * Numeric coercion: `data` rows may carry string values for the series columns (e.g. when\n * derived from CSV) — recharts needs numbers, so we coerce in-place. Already-numeric data\n * passes through unchanged.\n */\nexport function BarChart({\n data,\n xKey,\n series,\n stacked = false,\n height = 320,\n width = \"100%\",\n className,\n palette = CHART_PALETTE,\n showTooltip = true,\n showLegend,\n showGrid = true,\n showXAxis = true,\n showYAxis = true,\n showValueLabels,\n xAxisInterval = 0,\n xAxisAngle,\n xAxisTickFormatter,\n}: BarChartProps): ReactNode {\n const seriesKeys = useMemo(() => series.map((s) => s.key), [series]);\n // Memoised so non-artifact consumers passing inline `data` arrays don't re-allocate the\n // coerced copy on every parent render. For the artifact path `data` is already stable from\n // `useRowsArtifactData`, so this is a no-op there.\n const coercedData = useMemo(\n () => coerceNumericColumns(data as ReadonlyArray<Record<string, unknown>>, seriesKeys),\n [data, seriesKeys]\n );\n\n // Smart defaults — see prop JSDoc for rationale.\n const resolvedShowLegend = showLegend ?? series.length > 1;\n const resolvedShowValueLabels = showValueLabels ?? (!stacked && series.length === 1);\n // Auto-angle for dense bar charts: at 8+ categories the horizontal slot per label is too\n // narrow for full-text labels to fit. Rotating to -30° lets labels extend diagonally below\n // the axis without overlapping their neighbours. Consumer can override with `xAxisAngle={0}`.\n const resolvedAngle = xAxisAngle ?? (coercedData.length >= 8 ? -30 : 0);\n // Auto-truncating tick formatter — keeps every bar labeled while preventing overlap. The\n // tooltip shows the full unformatted value on hover, so no information is lost. Consumer\n // can opt out with `(v) => String(v)`.\n const resolvedTickFormatter = useMemo(\n () => xAxisTickFormatter ?? defaultBarTickFormatter(coercedData.length),\n [xAxisTickFormatter, coercedData.length]\n );\n\n // Angled labels need extra bottom margin so they don't clip the chart. ~0.7px per degree\n // covers up to -45° comfortably; we cap to avoid runaway when callers pass aggressive angles.\n const angledLabelPad = resolvedAngle === 0 ? 0 : Math.min(Math.abs(resolvedAngle) * 0.7, 40);\n const chartMargin = { ...CHART_MARGIN, bottom: CHART_MARGIN.bottom + angledLabelPad };\n\n return (\n <ChartContainer height={height} width={width} className={className}>\n <RechartsBarChart data={coercedData} margin={chartMargin} barCategoryGap=\"22%\">\n {showGrid ? <CartesianGrid {...CHART_GRID_PROPS} /> : null}\n {showXAxis ? (\n <XAxis\n dataKey={xKey}\n {...CHART_AXIS_PROPS}\n interval={xAxisInterval}\n angle={resolvedAngle}\n // Right-anchor angled labels so the end of each label aligns with its tick, rather\n // than the centre running off the axis. Middle-anchor for horizontal labels.\n textAnchor={resolvedAngle !== 0 ? \"end\" : \"middle\"}\n tickMargin={resolvedAngle === 0 ? 8 : 12}\n height={resolvedAngle !== 0 ? 60 : undefined}\n tickFormatter={resolvedTickFormatter}\n />\n ) : null}\n {showYAxis ? <YAxis {...CHART_AXIS_PROPS} width={40} /> : null}\n {showTooltip ? <Tooltip cursor={{ fill: BAR_TOOLTIP_CURSOR_FILL }} content={<ChartTooltipContent />} /> : null}\n {resolvedShowLegend ? (\n <Legend\n verticalAlign=\"top\"\n align=\"center\"\n // Pin the absolutely-positioned wrapper flush to the chart's top edge. Recharts'\n // default `top` for a top-aligned legend leaves a gap above the swatches; we'd\n // rather let `CHART_MARGIN.top` control the breathing room below the legend.\n wrapperStyle={{ top: 0 }}\n // Recharts 3.x defaults `itemSorter` to `'value'`, which alphabetically sorts legend\n // entries by label. The tooltip, the stack, and our chart's series colours all follow\n // declaration order — sorting the legend silently breaks correspondence (an \"Others\"\n // series declared last ends up mid-legend alphabetically). `null` disables the sort.\n itemSorter={null}\n content={<ChartLegendContent iconType=\"square\" />}\n />\n ) : null}\n {series.map((s, i) => (\n <Bar\n key={s.key}\n dataKey={s.key}\n name={s.label}\n fill={colorForSeriesIndex(i, palette)}\n stackId={stacked ? \"default\" : s.key}\n radius={barRadius(stacked, i, series.length)}\n >\n {resolvedShowValueLabels ? (\n <LabelList dataKey={s.key} position=\"top\" style={CHART_VALUE_LABEL_STYLE} />\n ) : null}\n </Bar>\n ))}\n </RechartsBarChart>\n </ChartContainer>\n );\n}\n\n/**\n * Per-corner border radius for a bar at position `seriesIndex` in `seriesCount` total series.\n *\n * - **Grouped (non-stacked)** — every series rounded at the top (`[6, 6, 0, 0]`). Bottom sits\n * on the X-axis, so flat bottom looks correct.\n * - **Stacked** — only the top-most segment gets `[6, 6, 0, 0]`; bottom + middle segments are\n * squared so the rect joins look seamless. Bottom segment is flat too because the chart's\n * X-axis is its base.\n */\nfunction barRadius(stacked: boolean, seriesIndex: number, seriesCount: number): [number, number, number, number] {\n if (!stacked) return [6, 6, 0, 0];\n const isTopSegment = seriesIndex === seriesCount - 1;\n return isTopSegment ? [6, 6, 0, 0] : [0, 0, 0, 0];\n}\n","import { type CSSProperties, type ReactElement, type ReactNode } from \"react\";\nimport { ResponsiveContainer } from \"recharts\";\n\nimport { cn } from \"@/lib/utils\";\n\nexport interface ChartContainerProps {\n /** A single recharts chart element — `<LineChart>`, `<BarChart>`, `<PieChart>`, etc. */\n children: ReactElement;\n /**\n * Container height. Recharts' `ResponsiveContainer` measures its parent, so we need a\n * concrete value here (not \"auto\"). Accepts a number (px) or any CSS length string.\n * Defaults to `320` — a sensible mid-density chart height.\n */\n height?: number | string;\n /**\n * Container width. Defaults to `\"100%\"` so the chart flexes to its parent. Pass a fixed\n * number when embedding in a layout that doesn't have a definite width (e.g. an auto-sized\n * flex column).\n */\n width?: number | string;\n /** Optional className spread on the outer wrapper. */\n className?: string;\n /** Optional inline style merged into the outer wrapper. `width`/`height` here take precedence. */\n style?: CSSProperties;\n}\n\n/**\n * The composable foundation for every clay chart. Wraps recharts' `ResponsiveContainer` in a\n * sized `<div>` so the chart has a definite parent box to measure against — without this, charts\n * silently collapse to 0×0 inside flex containers (see the `flex-basis` gotcha documented in\n * the chart-artifact orchestrator).\n *\n * Use directly when composing a chart from recharts primitives:\n *\n * ```tsx\n * <ChartContainer height={320}>\n * <LineChart data={data} margin={{ top: 8, right: 16, left: 0, bottom: 4 }}>\n * <CartesianGrid {...CHART_GRID_PROPS} />\n * <XAxis dataKey=\"month\" {...CHART_AXIS_PROPS} />\n * <YAxis {...CHART_AXIS_PROPS} />\n * <Tooltip content={<ChartTooltipContent />} />\n * <Legend content={<ChartLegendContent />} />\n * <Line dataKey=\"value\" stroke={CHART_PALETTE[0]} />\n * </LineChart>\n * </ChartContainer>\n * ```\n *\n * For common shapes, prefer the pre-composed `<BarChart>` / `<LineChart>` / `<PieChart>` —\n * they call `ChartContainer` under the hood with sensible defaults.\n */\nexport function ChartContainer({\n children,\n height = 320,\n width = \"100%\",\n className,\n style,\n}: ChartContainerProps): ReactNode {\n return (\n <div className={cn(\"text-xs text-foreground\", className)} style={{ width, height, ...style }}>\n <ResponsiveContainer width=\"100%\" height=\"100%\">\n {children}\n </ResponsiveContainer>\n </div>\n );\n}\n","import { type ReactNode } from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\n/**\n * Single entry in recharts' legend payload — one per series. Typed locally because recharts'\n * `LegendProps['payload']` is overly tagged-generic.\n */\ninterface LegendPayloadEntry {\n value?: string | number;\n color?: string;\n dataKey?: string | number;\n type?: string;\n}\n\n/**\n * Recharts injects `payload` at runtime when this component is used as the `content` slot on\n * `<Legend>`. Consumer-facing props (`className`, `iconType`) survive the cloneElement merge.\n */\nexport interface ChartLegendContentProps {\n /** Set by recharts at runtime — one entry per series. */\n payload?: LegendPayloadEntry[];\n /** Optional className merged onto the legend list. */\n className?: string;\n /** Visual indicator next to each label. `square` for bars/pies; `line` for line/area. Default `square`. */\n iconType?: \"square\" | \"line\";\n}\n\n/**\n * The default legend body for clay charts. Drop into any recharts `<Legend>`:\n *\n * ```tsx\n * <Legend content={<ChartLegendContent iconType=\"square\" />} />\n * <Legend content={<ChartLegendContent iconType=\"line\" />} />\n * ```\n *\n * Recharts also accepts `verticalAlign` / `align` on `<Legend>` itself for positioning —\n * `<ChartLegendContent>` is just the body. Style stays consistent regardless of position.\n */\nexport function ChartLegendContent({ payload, className, iconType = \"square\" }: ChartLegendContentProps): ReactNode {\n if (!payload || payload.length === 0) return null;\n\n return (\n <ul\n // No padding on this `<ul>`. Recharts wraps it in a `.recharts-legend-wrapper` div that's\n // positioned `absolute` with an inline `top` — padding here doesn't shift the wrapper's\n // anchor point, it only inflates the wrapper's height. The \"gap above the legend\" must be\n // controlled by the wrapper's inline `top`, set via each chart's `<Legend wrapperStyle>`.\n className={cn(\"flex flex-wrap items-center justify-center gap-x-4 gap-y-2 text-xs text-brown-80\", className)}\n >\n {payload.map((entry: LegendPayloadEntry, i: number) => {\n const swatchColor = typeof entry.color === \"string\" ? entry.color : \"transparent\";\n const label = entry.value !== undefined ? String(entry.value) : \"\";\n return (\n <li key={`${label}-${i}`} className=\"flex items-center gap-1.5\">\n {iconType === \"square\" ? (\n <span className=\"inline-block h-2.5 w-2.5 shrink-0 rounded-sm\" style={{ background: swatchColor }} />\n ) : (\n <span className=\"inline-block h-[3px] w-4 shrink-0 rounded-full\" style={{ background: swatchColor }} />\n )}\n <span>{label}</span>\n </li>\n );\n })}\n </ul>\n );\n}\n","import { type ReactNode } from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\n/**\n * Single entry in recharts' tooltip payload — one per series at the hovered x-tick.\n * Typed locally because recharts' `TooltipProps['payload']` is overly tagged-generic for this use.\n */\ninterface TooltipPayloadEntry {\n name?: string | number;\n value?: number | string;\n color?: string;\n dataKey?: string | number;\n}\n\n/**\n * Recharts injects `active`, `payload`, and `label` at runtime when this component is used as\n * the `content` slot on `<Tooltip>`. Consumer-facing props (`className`, `showLabel`,\n * `formatValue`) survive the cloneElement merge.\n */\nexport interface ChartTooltipContentProps {\n /** Set by recharts at runtime — true when the tooltip is hovered open. */\n active?: boolean;\n /** Set by recharts at runtime — one entry per series. */\n payload?: TooltipPayloadEntry[];\n /** Set by recharts at runtime — the x-tick label for the hovered column. */\n label?: string | number;\n /** Optional className merged onto the tooltip card. */\n className?: string;\n /** When false, the row label (typically the x-tick value) is hidden. Default true. */\n showLabel?: boolean;\n /** Override the formatter used on each entry's value. Defaults to `String(value)`. */\n formatValue?: (value: number | string | undefined, name: string | undefined) => string;\n}\n\n/**\n * The default tooltip card for clay charts. Drop into any recharts `<Tooltip>`:\n *\n * ```tsx\n * <Tooltip content={<ChartTooltipContent />} />\n * <Tooltip content={<ChartTooltipContent formatValue={(v) => `$${v}`} />} />\n * ```\n *\n * Renders a soft brown card with one row per series, swatch left, name center, value right.\n * Returns `null` when recharts says the tooltip is inactive — recharts mounts the content\n * component unconditionally and toggles it via the `active` prop.\n */\nexport function ChartTooltipContent({\n active,\n payload,\n label,\n className,\n showLabel = true,\n formatValue,\n}: ChartTooltipContentProps): ReactNode {\n if (!active || !payload || payload.length === 0) return null;\n\n return (\n <div\n className={cn(\n \"rounded-lg border border-brown-40 bg-brown-10 px-3 py-2 text-xs shadow-sm\",\n \"min-w-32 text-foreground\",\n className\n )}\n >\n {showLabel && label !== undefined && label !== null ? (\n <div className=\"mb-1.5 font-medium text-brown-90\">{String(label)}</div>\n ) : null}\n <ul className=\"space-y-1\">\n {payload.map((entry: TooltipPayloadEntry, i: number) => {\n const name = entry.name !== undefined ? String(entry.name) : \"\";\n const rawValue = entry.value;\n const display = formatValue ? formatValue(rawValue, name) : rawValue === undefined ? \"\" : String(rawValue);\n const swatch = typeof entry.color === \"string\" ? entry.color : \"transparent\";\n return (\n <li key={`${name}-${i}`} className=\"flex items-center gap-2\">\n <span className=\"inline-block h-2.5 w-2.5 shrink-0 rounded-sm\" style={{ background: swatch }} />\n <span className=\"text-brown-70\">{name}</span>\n <span className=\"ml-auto font-semibold tabular-nums text-brown-90\">{display}</span>\n </li>\n );\n })}\n </ul>\n </div>\n );\n}\n","export type { ColorToken } from \"./colors\";\nexport { colors } from \"./colors\";\nexport type { TypographyStyle, TypographyType } from \"./typography\";\nexport { fontFamilies, typographyMixin, typographyStyles, typographyTypes } from \"./typography\";\n","/** Available font families in the Shapes.co design system. */\nexport const fontFamilies = {\n /** Geist — primary UI font for body text, labels, headings, and display. */\n GEIST: \"Geist\",\n /** Crimson Pro — editorial/serif font, always italic. Used for decorative headings and pull quotes. */\n CRIMSON_PRO: \"Crimson Pro\",\n} as const;\n\nexport const typographyTypes = {\n // Geist Label Caption (12px)\n GEIST_LABEL_CAPTION_REGULAR: \"GEIST_LABEL_CAPTION_REGULAR\",\n GEIST_LABEL_CAPTION_MEDIUM: \"GEIST_LABEL_CAPTION_MEDIUM\",\n GEIST_LABEL_CAPTION_SEMI_BOLD: \"GEIST_LABEL_CAPTION_SEMI_BOLD\",\n\n // Geist Body Extra Small (14px)\n GEIST_BODY_XS_REGULAR: \"GEIST_BODY_XS_REGULAR\",\n GEIST_BODY_XS_MEDIUM: \"GEIST_BODY_XS_MEDIUM\",\n GEIST_BODY_XS_BOLD: \"GEIST_BODY_XS_BOLD\",\n\n // Geist Body Small (16px)\n GEIST_BODY_S_REGULAR: \"GEIST_BODY_S_REGULAR\",\n GEIST_BODY_S_MEDIUM: \"GEIST_BODY_S_MEDIUM\",\n GEIST_BODY_S_SEMI_BOLD: \"GEIST_BODY_S_SEMI_BOLD\",\n GEIST_BODY_S_BOLD: \"GEIST_BODY_S_BOLD\",\n\n // Geist Body Medium (18px)\n GEIST_BODY_M_LIGHT: \"GEIST_BODY_M_LIGHT\",\n GEIST_BODY_M_REGULAR: \"GEIST_BODY_M_REGULAR\",\n GEIST_BODY_M_MEDIUM: \"GEIST_BODY_M_MEDIUM\",\n GEIST_BODY_M_SEMI_BOLD: \"GEIST_BODY_M_SEMI_BOLD\",\n\n // Geist Body Large (20px)\n GEIST_BODY_L_REGULAR: \"GEIST_BODY_L_REGULAR\",\n GEIST_BODY_L_MEDIUM: \"GEIST_BODY_L_MEDIUM\",\n GEIST_BODY_L_SEMI_BOLD: \"GEIST_BODY_L_SEMI_BOLD\",\n GEIST_BODY_L_BOLD: \"GEIST_BODY_L_BOLD\",\n\n // Geist Heading Small (24px)\n GEIST_HEADING_S_REGULAR: \"GEIST_HEADING_S_REGULAR\",\n GEIST_HEADING_S_MEDIUM: \"GEIST_HEADING_S_MEDIUM\",\n GEIST_HEADING_S_BOLD: \"GEIST_HEADING_S_BOLD\",\n\n // Geist Heading Medium (28px)\n GEIST_HEADING_M_BOLD: \"GEIST_HEADING_M_BOLD\",\n\n // Geist Heading Large (32px)\n GEIST_HEADING_L_REGULAR: \"GEIST_HEADING_L_REGULAR\",\n GEIST_HEADING_L_MEDIUM: \"GEIST_HEADING_L_MEDIUM\",\n GEIST_HEADING_L_SEMI_BOLD: \"GEIST_HEADING_L_SEMI_BOLD\",\n GEIST_HEADING_L_BOLD: \"GEIST_HEADING_L_BOLD\",\n\n // Geist Display Large (36px)\n GEIST_DISPLAY_L_MEDIUM: \"GEIST_DISPLAY_L_MEDIUM\",\n\n // Crimson Pro Body Small (16px)\n CRIMSON_PRO_BODY_S_LIGHT: \"CRIMSON_PRO_BODY_S_LIGHT\",\n\n // Crimson Pro Body Medium (18px)\n CRIMSON_PRO_BODY_M_LIGHT: \"CRIMSON_PRO_BODY_M_LIGHT\",\n\n // Crimson Pro Heading Medium (24px)\n CRIMSON_PRO_HEADING_M_MEDIUM: \"CRIMSON_PRO_HEADING_M_MEDIUM\",\n\n // Crimson Pro Display Extra Large (46px)\n CRIMSON_PRO_DISPLAY_XL_REGULAR: \"CRIMSON_PRO_DISPLAY_XL_REGULAR\",\n} as const;\n\n/** Union of all typography type string constants. Used as keys into `typographyStyles`. */\nexport type TypographyType = (typeof typographyTypes)[keyof typeof typographyTypes];\n\n/** CSS properties for a single typography type. All numeric values are in pixels. */\nexport interface TypographyStyle {\n /** Font family name (e.g., \"Geist\"). */\n fontFamily: string;\n /** Font size in pixels. */\n fontSize: number;\n /** Font weight (100–900). */\n fontWeight: number;\n /** Line height in pixels. */\n lineHeight: number;\n /** Letter spacing in pixels. Omitted when the font's default spacing is correct. */\n letterSpacing?: number;\n /** CSS font-style value (e.g., \"italic\"). Omitted for normal style. */\n fontStyle?: string;\n}\n\nexport const typographyStyles: Record<TypographyType, TypographyStyle> = {\n // Geist Label Caption (12px)\n [typographyTypes.GEIST_LABEL_CAPTION_REGULAR]: {\n fontFamily: fontFamilies.GEIST,\n fontSize: 12,\n fontWeight: 400,\n lineHeight: 16,\n },\n [typographyTypes.GEIST_LABEL_CAPTION_MEDIUM]: {\n fontFamily: fontFamilies.GEIST,\n fontSize: 12,\n fontWeight: 500,\n lineHeight: 16,\n },\n [typographyTypes.GEIST_LABEL_CAPTION_SEMI_BOLD]: {\n fontFamily: fontFamilies.GEIST,\n fontSize: 12,\n fontWeight: 600,\n lineHeight: 16,\n },\n\n // Geist Body Extra Small (14px)\n [typographyTypes.GEIST_BODY_XS_REGULAR]: {\n fontFamily: fontFamilies.GEIST,\n fontSize: 14,\n fontWeight: 400,\n lineHeight: 20,\n },\n [typographyTypes.GEIST_BODY_XS_MEDIUM]: {\n fontFamily: fontFamilies.GEIST,\n fontSize: 14,\n fontWeight: 500,\n lineHeight: 20,\n },\n [typographyTypes.GEIST_BODY_XS_BOLD]: {\n fontFamily: fontFamilies.GEIST,\n fontSize: 14,\n fontWeight: 600,\n lineHeight: 20,\n },\n\n // Geist Body Small (16px)\n [typographyTypes.GEIST_BODY_S_REGULAR]: {\n fontFamily: fontFamilies.GEIST,\n fontSize: 16,\n fontWeight: 400,\n lineHeight: 24,\n letterSpacing: -0.08,\n },\n [typographyTypes.GEIST_BODY_S_MEDIUM]: {\n fontFamily: fontFamilies.GEIST,\n fontSize: 16,\n fontWeight: 500,\n lineHeight: 24,\n letterSpacing: -0.08,\n },\n [typographyTypes.GEIST_BODY_S_SEMI_BOLD]: {\n fontFamily: fontFamilies.GEIST,\n fontSize: 16,\n fontWeight: 600,\n lineHeight: 24,\n letterSpacing: -0.08,\n },\n [typographyTypes.GEIST_BODY_S_BOLD]: {\n fontFamily: fontFamilies.GEIST,\n fontSize: 16,\n fontWeight: 700,\n lineHeight: 24,\n letterSpacing: -0.08,\n },\n\n // Geist Body Medium (18px)\n [typographyTypes.GEIST_BODY_M_LIGHT]: {\n fontFamily: fontFamilies.GEIST,\n fontSize: 18,\n fontWeight: 300,\n lineHeight: 28,\n letterSpacing: -0.09,\n },\n [typographyTypes.GEIST_BODY_M_REGULAR]: {\n fontFamily: fontFamilies.GEIST,\n fontSize: 18,\n fontWeight: 400,\n lineHeight: 28,\n letterSpacing: -0.09,\n },\n [typographyTypes.GEIST_BODY_M_MEDIUM]: {\n fontFamily: fontFamilies.GEIST,\n fontSize: 18,\n fontWeight: 500,\n lineHeight: 28,\n letterSpacing: -0.09,\n },\n [typographyTypes.GEIST_BODY_M_SEMI_BOLD]: {\n fontFamily: fontFamilies.GEIST,\n fontSize: 18,\n fontWeight: 600,\n lineHeight: 28,\n letterSpacing: -0.09,\n },\n\n // Geist Body Large (20px)\n [typographyTypes.GEIST_BODY_L_REGULAR]: {\n fontFamily: fontFamilies.GEIST,\n fontSize: 20,\n fontWeight: 400,\n lineHeight: 30,\n letterSpacing: -0.2,\n },\n [typographyTypes.GEIST_BODY_L_MEDIUM]: {\n fontFamily: fontFamilies.GEIST,\n fontSize: 20,\n fontWeight: 500,\n lineHeight: 30,\n letterSpacing: -0.2,\n },\n [typographyTypes.GEIST_BODY_L_SEMI_BOLD]: {\n fontFamily: fontFamilies.GEIST,\n fontSize: 20,\n fontWeight: 600,\n lineHeight: 30,\n letterSpacing: -0.2,\n },\n [typographyTypes.GEIST_BODY_L_BOLD]: {\n fontFamily: fontFamilies.GEIST,\n fontSize: 20,\n fontWeight: 700,\n lineHeight: 30,\n letterSpacing: -0.2,\n },\n\n // Geist Heading Small (24px)\n [typographyTypes.GEIST_HEADING_S_REGULAR]: {\n fontFamily: fontFamilies.GEIST,\n fontSize: 24,\n fontWeight: 400,\n lineHeight: 32,\n letterSpacing: -0.48,\n },\n [typographyTypes.GEIST_HEADING_S_MEDIUM]: {\n fontFamily: fontFamilies.GEIST,\n fontSize: 24,\n fontWeight: 500,\n lineHeight: 32,\n letterSpacing: -0.48,\n },\n [typographyTypes.GEIST_HEADING_S_BOLD]: {\n fontFamily: fontFamilies.GEIST,\n fontSize: 24,\n fontWeight: 700,\n lineHeight: 32,\n letterSpacing: -0.48,\n },\n\n // Geist Heading Medium (28px)\n [typographyTypes.GEIST_HEADING_M_BOLD]: {\n fontFamily: fontFamilies.GEIST,\n fontSize: 28,\n fontWeight: 700,\n lineHeight: 36,\n letterSpacing: -0.6,\n },\n\n // Geist Heading Large (32px)\n [typographyTypes.GEIST_HEADING_L_REGULAR]: {\n fontFamily: fontFamilies.GEIST,\n fontSize: 32,\n fontWeight: 400,\n lineHeight: 40,\n letterSpacing: -0.64,\n },\n [typographyTypes.GEIST_HEADING_L_MEDIUM]: {\n fontFamily: fontFamilies.GEIST,\n fontSize: 32,\n fontWeight: 500,\n lineHeight: 40,\n letterSpacing: -0.64,\n },\n [typographyTypes.GEIST_HEADING_L_SEMI_BOLD]: {\n fontFamily: fontFamilies.GEIST,\n fontSize: 32,\n fontWeight: 600,\n lineHeight: 40,\n letterSpacing: -0.64,\n },\n [typographyTypes.GEIST_HEADING_L_BOLD]: {\n fontFamily: fontFamilies.GEIST,\n fontSize: 32,\n fontWeight: 700,\n lineHeight: 40,\n letterSpacing: -0.64,\n },\n\n // Geist Display Large (36px)\n [typographyTypes.GEIST_DISPLAY_L_MEDIUM]: {\n fontFamily: fontFamilies.GEIST,\n fontSize: 36,\n fontWeight: 500,\n lineHeight: 44,\n letterSpacing: -0.72,\n },\n\n // Crimson Pro Body Small (16px)\n [typographyTypes.CRIMSON_PRO_BODY_S_LIGHT]: {\n fontFamily: fontFamilies.CRIMSON_PRO,\n fontSize: 16,\n fontWeight: 300,\n lineHeight: 20,\n letterSpacing: -0.32,\n fontStyle: \"italic\",\n },\n\n // Crimson Pro Body Medium (18px)\n [typographyTypes.CRIMSON_PRO_BODY_M_LIGHT]: {\n fontFamily: fontFamilies.CRIMSON_PRO,\n fontSize: 18,\n fontWeight: 300,\n lineHeight: 28,\n letterSpacing: 0,\n fontStyle: \"italic\",\n },\n\n // Crimson Pro Heading Medium (24px)\n [typographyTypes.CRIMSON_PRO_HEADING_M_MEDIUM]: {\n fontFamily: fontFamilies.CRIMSON_PRO,\n fontSize: 24,\n fontWeight: 400,\n lineHeight: 32,\n letterSpacing: -0.48,\n fontStyle: \"italic\",\n },\n\n // Crimson Pro Display Extra Large (46px)\n [typographyTypes.CRIMSON_PRO_DISPLAY_XL_REGULAR]: {\n fontFamily: fontFamilies.CRIMSON_PRO,\n fontSize: 46,\n fontWeight: 400,\n lineHeight: 54,\n letterSpacing: -1.38,\n fontStyle: \"italic\",\n },\n};\n\n/**\n * Generates a CSS string for a given typography type, suitable for use in styled-components template literals.\n * @param type - The typography type constant (e.g., `typographyTypes.GEIST_BODY_S_REGULAR`).\n * @returns A semicolon-separated CSS string with font-family, font-size, font-weight, line-height, letter-spacing, and font-style.\n */\nexport function typographyMixin(type: TypographyType): string {\n const style = typographyStyles[type];\n return [\n `font-family: \"${style.fontFamily}\", sans-serif`,\n `font-size: ${style.fontSize}px`,\n `font-weight: ${style.fontWeight}`,\n `line-height: ${style.lineHeight}px`,\n `letter-spacing: ${style.letterSpacing != null ? `${style.letterSpacing}px` : \"inherit\"}`,\n `font-style: ${style.fontStyle ?? \"normal\"}`,\n ].join(\";\\n \");\n}\n","import { colors } from \"@/tokens\";\n\n/**\n * Single ordered color palette shared by every chart variant (`BarChart`, `LineChart`,\n * `PieChart`). A graduated walk down the blue family (`blue-600` → `blue-400` → `blue-300` →\n * `blue-100`), pivot into the indigo family (`indigo-400` → `indigo-500` → `indigo-600` →\n * `indigo-800`), and close on the deepest blue (`blue-800`).\n *\n * Design intent: every chart in the product sits in the same cool blue / indigo family so\n * adjacent charts on a dashboard read as a unified system rather than a kaleidoscope. The\n * deliberately narrow hue range (blue + indigo only — no warm accents) is the point: it\n * forecloses on rainbow-coded multi-series charts where the visual noise outweighs the\n * series-distinction it tries to provide.\n *\n * **Same palette for pie too**: pie slices read as parts of a whole, but they also share a\n * dashboard with bar/line charts — keeping one palette across every variant means a single\n * chat surface containing all three variants looks like one design system, not three.\n *\n * Nine entries — the v1 series cap. The palette cycles modulo its length for series counts\n * past nine via `colorForSeriesIndex`.\n */\nexport const CHART_PALETTE = [\n colors[\"blue-600\"], // #2563EB — primary\n colors[\"blue-400\"], // #60A5FA — mid\n colors[\"blue-300\"], // #93C5FD — light\n colors[\"blue-100\"], // #DBEAFE — very light\n colors[\"indigo-400\"], // #818CF8\n colors[\"indigo-500\"], // #6366F1\n colors[\"indigo-600\"], // #4F46E5\n colors[\"indigo-800\"], // #3730A3\n colors[\"blue-800\"], // #1E40AF — deepest\n] as const;\n\n/**\n * Slice color reserved for the \"Others\" bucket in pie charts. Neutral warm grey so it reads as\n * \"leftover / unattributed\" rather than a peer category. Pinning to a constant (rather than the\n * tail of `CHART_PALETTE`) means an Others slice always renders identically regardless of how\n * many named categories the data carries before it.\n */\nexport const OTHERS_SLICE_COLOR = colors[\"brown-60\"];\n\n/**\n * English fallback labels treated as the \"Others\" bucket — case-insensitive match.\n *\n * Consumers should pass their own localized list to `<PieChart>` via the `othersCategoryLabels`\n * prop (or via the artifact's `labels.othersCategoryLabels`) — in non-English UIs the agent\n * writes a translated bucket name (e.g. \"אחרים\", \"Otros\", \"Anderen\") and the consumer's i18n\n * layer is the only place that knows which strings to map to grey/last.\n */\nexport const DEFAULT_OTHERS_CATEGORY_LABELS: readonly string[] = [\"others\", \"other\"];\n\n/**\n * True when `value` matches one of `labels` (case-insensitive, trims whitespace). `labels` is\n * required — consumers pass either `DEFAULT_OTHERS_CATEGORY_LABELS` (English fallback) or their\n * own translated list. Pass an empty array to disable Others detection entirely.\n */\nexport function isOthersCategory(value: unknown, labels: readonly string[]): boolean {\n if (value === null || value === undefined) return false;\n if (labels.length === 0) return false;\n const normalized = String(value).trim().toLowerCase();\n for (const label of labels) {\n if (normalized === label.toLowerCase()) return true;\n }\n return false;\n}\n\n/**\n * Resolves the color for a series at `index`. Cycles deterministically past the palette length\n * so configurations with more series than colors still render rather than throwing. Pass an\n * alternate `palette` (e.g. a brand-specific override) to use something other than the default\n * `CHART_PALETTE`.\n */\nexport function colorForSeriesIndex(index: number, palette: readonly string[] = CHART_PALETTE): string {\n return palette[index % palette.length] ?? CHART_PALETTE[0];\n}\n","import { colors } from \"@/tokens\";\n\n/**\n * Shared visual constants for every chart variant. Two flavours of export:\n *\n * 1. Raw constants (`GRID_STROKE`, `AXIS_STROKE`, …) — for one-off uses or custom recharts trees.\n * 2. Preset prop bundles (`CHART_AXIS_PROPS`, `CHART_GRID_PROPS`) — spreadable into recharts\n * primitives so consumers can build composable charts without restating defaults:\n *\n * ```tsx\n * <CartesianGrid {...CHART_GRID_PROPS} />\n * <XAxis dataKey=\"x\" {...CHART_AXIS_PROPS} tickMargin={8} />\n * ```\n *\n * Recharts primitives must be inlined inside the chart tree — recharts identifies its slot\n * children by component reference (`child.type === XAxis`), so wrapping them in a custom React\n * component breaks dispatch. Constants and preset props are the right shape to share.\n */\n\n// ── raw color constants ──────────────────────────────────────────────────────────────────────\n\n/** Horizontal grid line color — kept soft so the data dominates the visual hierarchy. */\nexport const GRID_STROKE = colors[\"brown-40\"];\n\n/** Axis-line + axis-tick color. Lighter than the tick text so the axis frames rather than competes. */\nexport const AXIS_STROKE = colors[\"brown-50\"];\n\n/** Axis tick label text style. */\nexport const AXIS_TICK_STYLE = {\n fill: colors[\"brown-70\"],\n fontSize: 12,\n fontWeight: 500,\n} as const;\n\n/**\n * Text style for data-point value labels — bars (`<LabelList position=\"top\">`), line\n * data-point labels, and pie slice value labels all use this so labels read as one visual\n * family regardless of chart variant. Matches `AXIS_TICK_STYLE` for color/weight/size so\n * every secondary text element in a chart sits at the same hierarchy level.\n */\nexport const CHART_VALUE_LABEL_STYLE = {\n fill: colors[\"brown-70\"],\n fontSize: 12,\n fontWeight: 500,\n} as const;\n\n/** Cursor (hover guide) fill for bar tooltips. */\nexport const BAR_TOOLTIP_CURSOR_FILL = colors[\"brown-alpha-12\"];\n\n/** Active-dot ring fill on hovered line points — matches the chart background so the dot reads raised. */\nexport const LINE_ACTIVE_DOT_STROKE = colors[\"brown-10\"];\n\n/** Default recharts margin — leaves room for axis ticks/labels and the bottom legend. */\nexport const CHART_MARGIN = { top: 8, right: 16, left: 0, bottom: 4 } as const;\n\n/** Pie-specific margin — extra horizontal room to absorb outside slice labels. */\nexport const PIE_CHART_MARGIN = { top: 0, right: 24, left: 24, bottom: 4 } as const;\n\n// ── preset prop bundles ──────────────────────────────────────────────────────────────────────\n\n/**\n * Spreadable defaults for `<XAxis>` and `<YAxis>` — no axis line, no tick line, soft brown ticks.\n * Pass `dataKey` explicitly on `<XAxis>`; leave it off `<YAxis>`. Override anything per-instance.\n */\nexport const CHART_AXIS_PROPS = {\n stroke: AXIS_STROKE,\n tick: AXIS_TICK_STYLE,\n tickLine: false,\n axisLine: false,\n} as const;\n\n/**\n * Spreadable defaults for `<CartesianGrid>` — horizontal dashed lines only.\n */\nexport const CHART_GRID_PROPS = {\n stroke: GRID_STROKE,\n strokeDasharray: \"3 3\",\n vertical: false,\n} as const;\n","/**\n * Shared x-axis tick helpers used by both `BarChart` and `LineChart`. Pulled into one module so\n * the truncation thresholds and the truncation algorithm itself stay in one place — otherwise\n * each variant evolves its own slightly-different heuristic and the charts read inconsistently\n * side-by-side.\n *\n * Bar uses `defaultBarTickFormatter` (label per category, truncated when dense).\n * Line uses `defaultLineChartTickFormatter` (label only at evenly-distributed indices).\n * Both share the `maxTickCharsByCategoryCount` ladder and the `truncateLabel` ellipsis logic.\n */\n\n/**\n * Max characters per x-axis tick label, keyed by category count. The thresholds approximate the\n * available pixel slot per tick on a typical chat-card-width chart (~700px plot area) at the\n * default 12px tick font size. Sparse charts get no truncation; dense charts get aggressive\n * trimming so labels stop overlapping.\n *\n * Consumers can override the entire formatting via `xAxisTickFormatter` on either chart.\n */\nexport function maxTickCharsByCategoryCount(n: number): number {\n if (n <= 5) return 24; // effectively no truncation\n if (n <= 8) return 14;\n if (n <= 12) return 10;\n if (n <= 16) return 7;\n return 5;\n}\n\n/**\n * Truncates one label per the per-count cap. Trailing whitespace before the ellipsis is trimmed\n * so \"Human Resources\" with cap=7 becomes \"Human…\" not \"Human …\" (reads cleaner).\n */\nexport function truncateLabel(value: unknown, maxChars: number): string {\n const s = String(value ?? \"\");\n if (s.length <= maxChars) return s;\n return s.slice(0, Math.max(1, maxChars - 1)).trimEnd() + \"…\";\n}\n\n/**\n * Returns the set of data-point indices whose label should be visible. First and last always\n * included; the remaining (target - 2) are evenly distributed across the interior. Avoids the\n * \"cluster in the middle\" failure mode of recharts' built-in `preserveStartEnd`.\n *\n * Example for N=12, target=5: visible indices are {0, 3, 5, 8, 11} — 5 evenly-spaced labels.\n * Used by `LineChart` to thin x-axis labels at high data-point counts while keeping every data\n * point drawn.\n */\nexport function evenlyDistributedVisibleIndices(n: number, target: number): Set<number> {\n if (n <= target) {\n return new Set(Array.from({ length: n }, (_, i) => i));\n }\n const set = new Set<number>();\n const step = (n - 1) / (target - 1);\n for (let i = 0; i < target; i++) {\n set.add(Math.round(i * step));\n }\n return set;\n}\n\n/**\n * Per-category truncating formatter used as `BarChart`'s default `tickFormatter`. Every category\n * gets a label (no info loss from auto-skipping) but long labels get truncated by the\n * `maxTickCharsByCategoryCount` ladder so dense charts don't overlap.\n */\nexport function defaultBarTickFormatter(categoryCount: number): (value: unknown) => string {\n const max = maxTickCharsByCategoryCount(categoryCount);\n return (value) => truncateLabel(value, max);\n}\n\n/**\n * Per-index truncating formatter used as `LineChart`'s default `tickFormatter`. Returns the\n * (truncated) label only for indices in `visibleIndices`; the rest return `\"\"` so recharts\n * renders no text there. The data point itself still draws.\n *\n * Truncation is keyed by the *visible* tick count, not the total data-point count: LINE thins\n * its labels via `visibleIndices` (typically 5-7 visible ticks even at N=100+ data points), so\n * each rendered label has plenty of pixel room. Using `categoryCount` here would cap labels\n * like \"Dec 2022\" to 5 characters on a 50-month chart and clip them to \"Dec…\" even though only\n * ~5 ticks actually render. `BarChart` keeps using `categoryCount` because every category gets\n * a tick there — density really is data-point count.\n */\nexport function defaultLineChartTickFormatter(\n categoryCount: number,\n visibleIndices: Set<number>\n): (value: unknown, index: number) => string {\n const max = maxTickCharsByCategoryCount(visibleIndices.size || categoryCount);\n return (value, index) => {\n if (!visibleIndices.has(index)) return \"\";\n return truncateLabel(value, max);\n };\n}\n","/**\n * Returns a shallow copy of `rows` with the `numericKeys` columns coerced to `number`.\n * Cells that can't be parsed become `NaN`; `null` / `undefined` cells pass through untouched;\n * columns NOT in `numericKeys` pass through unchanged.\n *\n * Recharts needs `number` on `dataKey` resolution for `<Line>` / `<Bar>` / `<Pie>` — already-\n * numeric data is a no-op. The function exists for the CSV-derived case where values arrive\n * as strings. `NaN` propagates to recharts as a missing data point (zero-height bar, gap in\n * line, zero-area slice).\n */\nexport function coerceNumericColumns(\n rows: ReadonlyArray<Record<string, unknown>>,\n numericKeys: ReadonlyArray<string>\n): Array<Record<string, unknown>> {\n return rows.map((row) => {\n const out: Record<string, unknown> = { ...row };\n for (const k of numericKeys) {\n const v = out[k];\n if (v == null) continue;\n const n = Number(v);\n out[k] = Number.isFinite(n) ? n : NaN;\n }\n return out;\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, useMemo } from \"react\";\nimport {\n CartesianGrid,\n LabelList,\n Legend,\n Line,\n LineChart as RechartsLineChart,\n Tooltip,\n XAxis,\n YAxis,\n} from \"recharts\";\n\nimport { ChartContainer } from \"../chart-container\";\nimport { ChartLegendContent } from \"../chart-legend-content\";\nimport { ChartTooltipContent } from \"../chart-tooltip-content\";\nimport { CHART_PALETTE, colorForSeriesIndex } from \"../chart-palette\";\nimport {\n CHART_AXIS_PROPS,\n CHART_GRID_PROPS,\n CHART_MARGIN,\n CHART_VALUE_LABEL_STYLE,\n LINE_ACTIVE_DOT_STROKE,\n} from \"../chart-styles\";\nimport { defaultLineChartTickFormatter, evenlyDistributedVisibleIndices } from \"../chart-tick-utils\";\nimport { coerceNumericColumns } from \"../coerce-numeric\";\nimport type { ChartSeries } from \"../types\";\n\nexport interface LineChartProps {\n /** Rows to plot. Each row should carry the `xKey` column and one column per `series[].key`. */\n data: ReadonlyArray<Record<string, unknown>>;\n /** Data column for the categorical/temporal x-axis. */\n xKey: string;\n /** One descriptor per line series. */\n series: ChartSeries[];\n /** Render monotone-curve lines instead of straight segments. **Default true** — smooth\n * curves look cleaner for typical chat / dashboard usage. Pass `false` for true linear\n * interpolation (e.g. step-like data where the literal value at each point matters). */\n smooth?: boolean;\n /** Show data-point dots. **Default true** — dots anchor each bin of the line so the viewer\n * can read individual values at a glance. Pass `false` for a pure trend line (dense data sets\n * where dots would crowd, or styling preference for unembellished strokes). */\n showDots?: boolean;\n /** Line stroke width in px. Default 2. */\n strokeWidth?: number;\n /** Container height. Default 320. */\n height?: number | string;\n /** Container width. Default `\"100%\"`. */\n width?: number | string;\n /** Optional className on the outer container. */\n className?: string;\n /** Override the default palette. Colors are assigned by series index. */\n palette?: readonly string[];\n /** Show the tooltip on hover. Default true. */\n showTooltip?: boolean;\n /**\n * Show the legend at the top. **Default behavior**: auto-hidden for single-series charts\n * (the legend would just label the one and only color, which is noise). Pass `true` to force\n * the legend even with one series; pass `false` to suppress for multi-series.\n */\n showLegend?: boolean;\n /** Show horizontal dashed grid lines. Default true. */\n showGrid?: boolean;\n /** Show the x-axis. Default true. */\n showXAxis?: boolean;\n /** Show the y-axis. Default true. */\n showYAxis?: boolean;\n /**\n * Show numeric value labels next to each data point. **Default off** — for line charts with\n * many data points (12+ months × N series) labels overlap quickly. Set `true` for single-series\n * charts with sparse data (e.g. ≤8 points) where the labels stay readable.\n */\n showValueLabels?: boolean;\n /**\n * Recharts XAxis tick `interval`. **Default**: `0` (every tick rendered) — clay's tick\n * formatter blanks out non-key labels so the visible labels stay evenly distributed.\n * Override to a fixed number to force \"every Nth label\", or to a recharts string preset\n * like `\"preserveStartEnd\"` to let recharts pick.\n */\n xAxisInterval?: number | \"preserveStart\" | \"preserveEnd\" | \"preserveStartEnd\";\n /**\n * Rotate x-axis tick labels by this many degrees. Negative values tilt up-and-right (e.g.\n * `-30`). Default `0`. When non-zero, the chart auto-bumps its bottom margin.\n */\n xAxisAngle?: number;\n /**\n * Custom formatter for x-axis tick labels. **Default**: clay shows only ~5 evenly-distributed\n * labels (first + last + ~3 middle), and auto-truncates each shown label so dense datasets\n * stay readable. Pass an explicit formatter to override the per-label rendering — to also\n * change which labels are visible, pass `xAxisInterval` and a formatter that returns the\n * value for every index.\n *\n * The tooltip continues to show the full unformatted value on hover regardless.\n */\n xAxisTickFormatter?: (value: unknown) => string;\n}\n\n/**\n * Target visible x-axis label count for line charts. Recharts' built-in `preserveStartEnd`\n * picks labels by collision detection, which produces uneven clusters when label widths vary.\n * Picking a fixed target and distributing evenly gives a cleaner result and the same value at\n * any chart width (the chart still shows every data point as a dot — only labels thin out).\n */\nconst TARGET_VISIBLE_LABELS = 5;\n\n/**\n * Pre-composed line chart. Single-call usage for the common case; for custom recharts trees\n * compose `<ChartContainer>` + recharts primitives directly.\n *\n * ```tsx\n * <LineChart\n * data={[{ month: \"Jan\", value: 120 }, { month: \"Feb\", value: 132 }]}\n * xKey=\"month\"\n * series={[{ key: \"value\", label: \"Headcount\" }]}\n * smooth\n * />\n * ```\n *\n * **Smart defaults**: legend auto-hides for single-series; value labels default off (turn on\n * for sparse single-series charts); x-axis shows every tick by default.\n */\nexport function LineChart({\n data,\n xKey,\n series,\n smooth = true,\n showDots = true,\n strokeWidth = 2,\n height = 320,\n width = \"100%\",\n className,\n palette = CHART_PALETTE,\n showTooltip = true,\n showLegend,\n showGrid = true,\n showXAxis = true,\n showYAxis = true,\n showValueLabels = false,\n xAxisInterval = 0,\n xAxisAngle = 0,\n xAxisTickFormatter,\n}: LineChartProps): ReactNode {\n const seriesKeys = useMemo(() => series.map((s) => s.key), [series]);\n // Memoised so non-artifact consumers passing inline `data` arrays don't re-allocate the\n // coerced copy on every parent render.\n const coercedData = useMemo(\n () => coerceNumericColumns(data as ReadonlyArray<Record<string, unknown>>, seriesKeys),\n [data, seriesKeys]\n );\n const lineType = smooth ? \"monotone\" : \"linear\";\n\n const resolvedShowLegend = showLegend ?? series.length > 1;\n // Default: only ~5 evenly distributed labels are visible (first + last + middle), each\n // truncated to fit. Consumer can override with `xAxisTickFormatter` (their formatter is\n // applied to every label without thinning). See `defaultLineChartTickFormatter`.\n const resolvedTickFormatter = useMemo(() => {\n if (xAxisTickFormatter) return xAxisTickFormatter;\n const visible = evenlyDistributedVisibleIndices(coercedData.length, TARGET_VISIBLE_LABELS);\n return defaultLineChartTickFormatter(coercedData.length, visible);\n }, [xAxisTickFormatter, coercedData.length]);\n const angledLabelPad = xAxisAngle === 0 ? 0 : Math.min(Math.abs(xAxisAngle) * 0.7, 40);\n // Extra right margin so the rightmost x-axis tick label has room to render its right half\n // without getting clipped at the chart edge. Recharts centres tick labels under their tick;\n // without padding, ~half of the last label spills past the plot area.\n const chartMargin = { ...CHART_MARGIN, right: CHART_MARGIN.right + 16, bottom: CHART_MARGIN.bottom + angledLabelPad };\n\n return (\n <ChartContainer height={height} width={width} className={className}>\n <RechartsLineChart data={coercedData} margin={chartMargin}>\n {showGrid ? <CartesianGrid {...CHART_GRID_PROPS} /> : null}\n {showXAxis ? (\n <XAxis\n dataKey={xKey}\n {...CHART_AXIS_PROPS}\n interval={xAxisInterval}\n angle={xAxisAngle}\n textAnchor={xAxisAngle !== 0 ? \"end\" : \"middle\"}\n tickMargin={xAxisAngle === 0 ? 8 : 12}\n height={xAxisAngle !== 0 ? 60 : undefined}\n tickFormatter={resolvedTickFormatter}\n />\n ) : null}\n {showYAxis ? <YAxis {...CHART_AXIS_PROPS} width={40} /> : null}\n {showTooltip ? <Tooltip content={<ChartTooltipContent />} /> : null}\n {resolvedShowLegend ? (\n <Legend\n verticalAlign=\"top\"\n align=\"center\"\n // Pin the absolutely-positioned wrapper flush to the chart's top edge — see `BarChart`\n // for the rationale.\n wrapperStyle={{ top: 0 }}\n // Preserve declaration order; disable recharts 3.x's `'value'` alphabetical sort.\n // See `BarChart` for the rationale.\n itemSorter={null}\n content={<ChartLegendContent iconType=\"line\" />}\n />\n ) : null}\n {series.map((s, i) => {\n const color = colorForSeriesIndex(i, palette);\n return (\n <Line\n key={s.key}\n type={lineType}\n dataKey={s.key}\n name={s.label}\n stroke={color}\n strokeWidth={strokeWidth}\n // Filled dot matching the line color — recharts default is hollow (white fill +\n // colored stroke), which reads as \"annotation\" rather than \"data point\".\n dot={showDots ? { r: 3, fill: color, stroke: color, strokeWidth: 0 } : false}\n activeDot={{ r: 5, fill: color, stroke: LINE_ACTIVE_DOT_STROKE, strokeWidth: 2 }}\n >\n {showValueLabels ? <LabelList dataKey={s.key} position=\"top\" style={CHART_VALUE_LABEL_STYLE} /> : null}\n </Line>\n );\n })}\n </RechartsLineChart>\n </ChartContainer>\n );\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, useMemo } from \"react\";\nimport { Cell, Legend, Pie, PieChart as RechartsPieChart, Tooltip } from \"recharts\";\n\nimport { ChartContainer } from \"../chart-container\";\nimport { ChartLegendContent } from \"../chart-legend-content\";\nimport { ChartTooltipContent } from \"../chart-tooltip-content\";\nimport {\n CHART_PALETTE,\n DEFAULT_OTHERS_CATEGORY_LABELS,\n OTHERS_SLICE_COLOR,\n colorForSeriesIndex,\n isOthersCategory,\n} from \"../chart-palette\";\nimport { CHART_VALUE_LABEL_STYLE, PIE_CHART_MARGIN } from \"../chart-styles\";\nimport { coerceNumericColumns } from \"../coerce-numeric\";\n\n/**\n * Recharts injects geometric position props onto label render functions. Typed locally because\n * recharts' `PieLabelProps` is overly tagged-generic.\n */\ninterface PieLabelRenderProps {\n /** Computed x coordinate for the label (outside the slice). */\n x?: number;\n /** Computed y coordinate for the label. */\n y?: number;\n /** \"start\" / \"middle\" / \"end\" / \"inherit\" — pre-computed for legibility outside the slice. */\n textAnchor?: \"start\" | \"middle\" | \"end\" | \"inherit\";\n /** The slice's numeric value (the value of `valueKey` on the row). */\n value?: unknown;\n}\n\nexport interface PieChartProps {\n /** Rows to plot. Each row maps to one slice. */\n data: ReadonlyArray<Record<string, unknown>>;\n /** Data column used as the slice label / legend entry. */\n categoryKey: string;\n /** Data column used as the slice numeric value. */\n valueKey: string;\n /** Container height. Default 320. */\n height?: number | string;\n /** Container width. Default `\"100%\"`. */\n width?: number | string;\n /** Optional className on the outer container. */\n className?: string;\n /** Override the default palette. Default `CHART_PALETTE` — the shared blue + indigo graduated\n * palette every chart variant uses. Override per-instance for brand-specific colours. */\n palette?: readonly string[];\n /** Show value labels next to each slice. Default true. */\n showLabels?: boolean;\n /** Show the tooltip on hover. Default true. */\n showTooltip?: boolean;\n /** Show the legend (category names). Default true. Position controlled by `legendPosition`. */\n showLegend?: boolean;\n /** Inner radius — `0` (default) for a pie; `>0` (e.g. `\"40%\"`) for a donut. */\n innerRadius?: number | string;\n /** Outer radius. Default `\"68%\"` — leaves room for outside value labels. */\n outerRadius?: number | string;\n /**\n * Category labels treated as the \"Others\" bucket (case-insensitive match). Matching slices are\n * pinned to the end of the sweep regardless of value and rendered in neutral grey\n * (`OTHERS_SLICE_COLOR`).\n *\n * **Default**: `DEFAULT_OTHERS_CATEGORY_LABELS` (English \"Others\" / \"Other\"). Pass a localized\n * list in non-English UIs — e.g. `[\"אחרים\"]` for Hebrew or `[\"Otros\", \"Otro\"]` for Spanish.\n * Pass `[]` to disable Others detection entirely (every slice colored from the palette).\n */\n othersCategoryLabels?: readonly string[];\n}\n\n/**\n * Pre-composed pie chart. One slice per row, colored by row index from the palette. Outside\n * value labels (no connector line); legend at the top. Set `innerRadius` to make a donut.\n *\n * **Sweep**: starts at 12 o'clock and proceeds clockwise (`startAngle={90}` / `endAngle={-270}`).\n * Recharts' default starts at 3 o'clock counter-clockwise, which feels arbitrary for a\n * data-first composition.\n *\n * **\"Others\" bucket**: any slice whose category label matches \"Others\" / \"Other\"\n * (case-insensitive — see `isOthersCategory`) is pinned to the end of the sweep regardless of\n * value, and rendered in neutral grey (`OTHERS_SLICE_COLOR`) so it reads as \"unattributed\n * leftover\" rather than a peer category. Other slices retain their incoming order.\n *\n * ```tsx\n * <PieChart\n * data={[\n * { department: \"Engineering\", headcount: 42 },\n * { department: \"Sales\", headcount: 18 },\n * { department: \"Others\", headcount: 5 },\n * ]}\n * categoryKey=\"department\"\n * valueKey=\"headcount\"\n * />\n *\n * // Donut variant\n * <PieChart data={...} categoryKey=\"...\" valueKey=\"...\" innerRadius=\"40%\" />\n * ```\n */\nexport function PieChart({\n data,\n categoryKey,\n valueKey,\n height = 320,\n width = \"100%\",\n className,\n palette = CHART_PALETTE,\n showLabels = true,\n showTooltip = true,\n showLegend = true,\n innerRadius = 0,\n outerRadius = \"68%\",\n othersCategoryLabels = DEFAULT_OTHERS_CATEGORY_LABELS,\n}: PieChartProps): ReactNode {\n // Memoised so non-artifact consumers passing inline `data` arrays don't re-allocate the\n // coerced + reordered copy on every parent render. Three steps:\n // 1. Coerce the `valueKey` column to numeric (CSV-derived rows arrive as strings).\n // 2. Stable-partition: keep non-Others slices in their original order, then append any\n // Others slices at the tail — preserves the agent's intended ordering for the named\n // categories while guaranteeing \"Others\" sits at the end of the sweep regardless of size.\n // 3. Compute per-slice colors keyed by the **named-slice index** (not the position in the\n // reordered array). Without this, an Others row added or removed in the middle of the\n // data would visibly shift the palette colors of every named slice that came after it.\n // Indexing by \"Nth named slice\" keeps each named category's color stable across data\n // edits / `othersCategoryLabels` updates.\n const { orderedData, sliceColors } = useMemo(() => {\n const coerced = coerceNumericColumns(data as ReadonlyArray<Record<string, unknown>>, [valueKey]);\n const namedRows: Record<string, unknown>[] = [];\n const namedColors: string[] = [];\n const othersRows: Record<string, unknown>[] = [];\n const othersColors: string[] = [];\n for (const row of coerced) {\n if (isOthersCategory(row[categoryKey], othersCategoryLabels)) {\n othersRows.push(row);\n othersColors.push(OTHERS_SLICE_COLOR);\n } else {\n namedRows.push(row);\n namedColors.push(colorForSeriesIndex(namedRows.length - 1, palette));\n }\n }\n return {\n orderedData: [...namedRows, ...othersRows],\n sliceColors: [...namedColors, ...othersColors],\n };\n }, [data, valueKey, categoryKey, othersCategoryLabels, palette]);\n\n return (\n <ChartContainer height={height} width={width} className={className}>\n <RechartsPieChart margin={PIE_CHART_MARGIN}>\n {showTooltip ? <Tooltip content={<ChartTooltipContent />} /> : null}\n {showLegend ? (\n <Legend\n verticalAlign=\"top\"\n align=\"center\"\n // Pin the absolutely-positioned wrapper flush to the chart's top edge — see `BarChart`\n // for the rationale.\n wrapperStyle={{ top: 0 }}\n // Preserve slice order; disable recharts 3.x's `'value'` alphabetical sort. Critical\n // for pie because the Others bucket is intentionally pinned to the end of the sweep\n // — sorting alphabetically would break that contract.\n itemSorter={null}\n content={<ChartLegendContent iconType=\"square\" />}\n />\n ) : null}\n <Pie\n data={orderedData}\n dataKey={valueKey}\n nameKey={categoryKey}\n cx=\"50%\"\n cy=\"52%\"\n // Start at 12 o'clock and sweep clockwise. Recharts' default (`0` / `360`) starts at\n // 3 o'clock counter-clockwise. The +90 / -270 pair preserves the full 360° arc length.\n startAngle={90}\n endAngle={-270}\n innerRadius={innerRadius}\n outerRadius={outerRadius}\n label={\n showLabels\n ? (entry: PieLabelRenderProps) => {\n const v = entry.value;\n if (v === undefined || v === null) return null;\n // Render the label as a `<text>` so we can pin its `fill` to the shared\n // `CHART_VALUE_LABEL_STYLE` instead of letting recharts auto-color it to\n // the slice's fill (which on light-blue slices becomes near-invisible).\n // Recharts passes the computed `x`/`y`/`textAnchor` so positioning is\n // unchanged.\n return (\n <text\n x={entry.x}\n y={entry.y}\n fill={CHART_VALUE_LABEL_STYLE.fill}\n fontSize={CHART_VALUE_LABEL_STYLE.fontSize}\n fontWeight={CHART_VALUE_LABEL_STYLE.fontWeight}\n textAnchor={entry.textAnchor}\n dominantBaseline=\"central\"\n >\n {String(v)}\n </text>\n );\n }\n : false\n }\n // No connector line — outside labels sit clean next to their slice. The thin\n // brown line between slice and label adds visual noise without information gain.\n labelLine={false}\n // No stroke between slices — gradient palette + adjacent contiguous segments read\n // as one continuous wheel rather than discrete cards with cream gaps between them.\n stroke=\"none\"\n strokeWidth={0}\n >\n {orderedData.map((_row, i) => (\n <Cell key={i} fill={sliceColors[i]} />\n ))}\n </Pie>\n </RechartsPieChart>\n </ChartContainer>\n );\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 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 { 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 * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\n/** Extension of shadcn's `Table` registry component: exposes `containerClassName` so callers can\n * override the wrapping `<div>`'s default `overflow-auto`. We need this for sticky-header support\n * (sticky uses the nearest overflow ancestor as its containing block — when the actual scroll\n * container is one level out, the inner overflow-auto needs to be neutralised with\n * `overflow-visible` so sticky aligns with the real scrolling parent). */\nconst Table = React.forwardRef<\n HTMLTableElement,\n React.HTMLAttributes<HTMLTableElement> & { containerClassName?: string }\n>(({ className, containerClassName, ...props }, ref) => (\n <div className={cn(\"relative w-full overflow-auto\", containerClassName)}>\n <table ref={ref} className={cn(\"w-full caption-bottom text-sm\", className)} {...props} />\n </div>\n));\nTable.displayName = \"Table\";\n\nconst TableHeader = React.forwardRef<HTMLTableSectionElement, React.HTMLAttributes<HTMLTableSectionElement>>(\n ({ className, ...props }, ref) => <thead ref={ref} className={cn(\"[&_tr]:border-b\", className)} {...props} />\n);\nTableHeader.displayName = \"TableHeader\";\n\nconst TableBody = React.forwardRef<HTMLTableSectionElement, React.HTMLAttributes<HTMLTableSectionElement>>(\n ({ className, ...props }, ref) => (\n <tbody ref={ref} className={cn(\"[&_tr:last-child]:border-0\", className)} {...props} />\n )\n);\nTableBody.displayName = \"TableBody\";\n\nconst TableFooter = React.forwardRef<HTMLTableSectionElement, React.HTMLAttributes<HTMLTableSectionElement>>(\n ({ className, ...props }, ref) => (\n <tfoot ref={ref} className={cn(\"border-t bg-muted/50 font-medium [&>tr]:last:border-b-0\", className)} {...props} />\n )\n);\nTableFooter.displayName = \"TableFooter\";\n\nconst TableRow = React.forwardRef<HTMLTableRowElement, React.HTMLAttributes<HTMLTableRowElement>>(\n ({ className, ...props }, ref) => (\n <tr\n ref={ref}\n className={cn(\"border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted\", className)}\n {...props}\n />\n )\n);\nTableRow.displayName = \"TableRow\";\n\nconst TableHead = React.forwardRef<HTMLTableCellElement, React.ThHTMLAttributes<HTMLTableCellElement>>(\n ({ className, ...props }, ref) => (\n <th\n ref={ref}\n className={cn(\n \"h-12 px-4 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0\",\n className\n )}\n {...props}\n />\n )\n);\nTableHead.displayName = \"TableHead\";\n\nconst TableCell = React.forwardRef<HTMLTableCellElement, React.TdHTMLAttributes<HTMLTableCellElement>>(\n ({ className, ...props }, ref) => (\n <td ref={ref} className={cn(\"p-4 align-middle [&:has([role=checkbox])]:pr-0\", className)} {...props} />\n )\n);\nTableCell.displayName = \"TableCell\";\n\nconst TableCaption = React.forwardRef<HTMLTableCaptionElement, React.HTMLAttributes<HTMLTableCaptionElement>>(\n ({ className, ...props }, ref) => (\n <caption ref={ref} className={cn(\"mt-4 text-sm text-muted-foreground\", className)} {...props} />\n )\n);\nTableCaption.displayName = \"TableCaption\";\n\nexport { Table, TableHeader, TableBody, TableFooter, TableHead, TableRow, TableCell, TableCaption };\n","import { useEffect, useRef, useState, type RefObject } from \"react\";\n\n/**\n * Tracks whether a scroll container has scrolled past its top edge, for sticky-header shadow effects.\n *\n * Returns `targetRef` (attach to the scroll container itself or any element inside it) and\n * `isScrolled` (true once the container's `scrollTop > 0`). On mount the hook walks up the DOM\n * from the target to find the nearest overflow ancestor (or uses the target itself if it already\n * has `overflowY: auto | scroll`), then listens to its `scroll` event.\n *\n * Used to surface a subtle \"scroll lift\" shadow under sticky headers so users know there is\n * content hidden above.\n *\n * **Why a scroll listener and not a sentinel + IntersectionObserver:** an earlier version used a\n * 1px sentinel as the first child of the scroll container. The sentinel's height pushed the thead\n * down by one pixel in normal flow, and as soon as the user scrolled the thead snapped up to its\n * sticky position — a visible 1px jitter. A scroll listener has no layout footprint.\n *\n * `passive: true` so the listener doesn't block native scroll smoothness; we never call\n * `preventDefault`.\n */\nexport function useScrollShadow(): {\n targetRef: RefObject<HTMLDivElement | null>;\n isScrolled: boolean;\n} {\n const targetRef = useRef<HTMLDivElement>(null);\n const [isScrolled, setIsScrolled] = useState(false);\n\n useEffect(() => {\n const target = targetRef.current;\n if (!target) return;\n\n let scrollContainer: HTMLElement | null = target;\n while (scrollContainer) {\n const overflowY = getComputedStyle(scrollContainer).overflowY;\n if (overflowY === \"auto\" || overflowY === \"scroll\") break;\n scrollContainer = scrollContainer.parentElement;\n }\n if (!scrollContainer) return;\n\n setIsScrolled(scrollContainer.scrollTop > 0);\n\n const handleScroll = () => {\n if (!scrollContainer) return;\n setIsScrolled(scrollContainer.scrollTop > 0);\n };\n scrollContainer.addEventListener(\"scroll\", handleScroll, { passive: true });\n return () => scrollContainer.removeEventListener(\"scroll\", handleScroll);\n }, []);\n\n return { targetRef, isScrolled };\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 { 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 { 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 { 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 { 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};\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","import type { ComponentType, ReactNode } from \"react\";\n\nimport { ArtifactServices } from \"@/components/artifacts/artifact-services\";\nimport type { ArtifactRefBlockRecord, BlockComponentProps } from \"../types\";\n\n/**\n * Renders an `ARTIFACT_REF` block by dispatching the inlined artifact to its per-type component.\n *\n * The cast at the seam is necessary because the registry-based dispatch can't statically prove\n * type-T narrowing — but it's not hiding a bug. `ArtifactRefBlockRecord` is a distributed union\n * over `ArtifactRecord` branches (see `blocks/types.ts`), so TS catches wrong-shape labels at the\n * consumer's construction site: a chart-labels bag assigned to a TABLE block's payload is a type\n * error there, not silently forwarded to the wrong renderer here.\n *\n * Unknown artifact types render as `null` — the map only contains types we know how to render,\n * so an older clay client receiving a future artifact type degrades silently rather than\n * crashing the chat.\n */\nexport function ArtifactRefBlock({ block }: BlockComponentProps<ArtifactRefBlockRecord>): ReactNode {\n const { artifact, labels, callbacks } = block.payload;\n const entry = ArtifactServices[artifact.type];\n if (!entry) return null;\n // The registry stores `ArtifactService<ArtifactRecord>` (the union), so `entry.Component` is\n // `ComponentType<ArtifactServiceProps<ArtifactRecord>>`. The labels/callbacks props in the\n // narrower per-type `ArtifactServiceProps<TArtifact>` are `Partial<ArtifactLabelsOf<TArtifact>>`\n // which the union doesn't satisfy positionally. Cast forwards the values verbatim — the\n // runtime invariant is provably correct because the distributed `ArtifactRefBlockRecord` type\n // enforces it at construction.\n const Component = entry.Component as ComponentType<{\n artifact: typeof artifact;\n labels?: typeof labels;\n callbacks?: typeof callbacks;\n }>;\n return <Component artifact={artifact} labels={labels} callbacks={callbacks} />;\n}\n","import { artifactToClipboardText } from \"@/components/artifacts/artifact-services\";\nimport { ArtifactRefBlock } from \"../artifact-ref-block/artifact-ref-block\";\nimport { blockTypes } from \"../types\";\nimport type { ArtifactRefBlockRecord, BlockService } from \"../types\";\n\nexport const ArtifactRefBlockService: BlockService<ArtifactRefBlockRecord> = {\n type: blockTypes.ARTIFACT_REF,\n Component: ArtifactRefBlock,\n /**\n * Delegates to the registered artifact component's `toClipboardText`. Falls back to\n * `[Artifact: <title>]` via `artifactToClipboardText`'s own fallback when the artifact type\n * has no registered component, so copy never silently produces an empty string.\n */\n toClipboardText: (block) => artifactToClipboardText(block.payload.artifact),\n};\n","const PLACEHOLDER_PREFIX = \"\\u200B\\u200BIC\";\nconst PLACEHOLDER_SUFFIX = \"\\u200B\\u200B\";\nconst PLACEHOLDER_REGEX = new RegExp(`${PLACEHOLDER_PREFIX}(\\\\d+)${PLACEHOLDER_SUFFIX}`, \"g\");\n\n/**\n * Strips only inline markdown marks (bold, italic, strikethrough) from a string.\n * Does not touch block-level syntax (headings, blockquotes, horizontal rules, links, images)\n * so values like `> 100`, `# items`, or `---` are preserved as-is.\n * @param text - The text to strip inline marks from.\n * @returns The text with inline marks removed.\n */\nexport function stripInlineMarks(text: string): string {\n return (\n text\n // Bold + italic\n .replace(/\\*{3}(.+?)\\*{3}/g, \"$1\")\n // Bold\n .replace(/\\*{2}(.+?)\\*{2}/g, \"$1\")\n .replace(/_{2}(.+?)_{2}/g, \"$1\")\n // Italic\n .replace(/\\*(.+?)\\*/g, \"$1\")\n .replace(/_(.+?)_/g, \"$1\")\n // Strikethrough\n .replace(/~~(.+?)~~/g, \"$1\")\n );\n}\n\n/**\n * Converts a Markdown string to plain text by stripping all formatting syntax.\n *\n * Handles headings, bold/italic/strikethrough, links, images, blockquotes, code blocks,\n * inline code, tables, and horizontal rules. Inline code content is preserved literally\n * (formatting inside backticks is not stripped).\n *\n * @param markdown - The Markdown source string.\n * @returns A plain-text string with all Markdown syntax removed.\n */\nexport function markdownToPlainText(markdown: string): string {\n if (!markdown) return \"\";\n\n // Protect inline code content from bold/italic stripping by replacing with placeholders\n const inlineCodeSnippets: string[] = [];\n let result = markdown\n // HTML comments (e.g. <!-- table-title: ... -->), including multiline and --!> variants\n .replace(/^<!--[\\s\\S]*?--!?>\\n?/gm, \"\")\n // Code blocks (must come first)\n .replace(/```[\\s\\S]*?```/g, \"\")\n // Inline code → placeholder\n .replace(/`([^`]+)`/g, (_match, code: string) => {\n inlineCodeSnippets.push(code);\n return `${PLACEHOLDER_PREFIX}${inlineCodeSnippets.length - 1}${PLACEHOLDER_SUFFIX}`;\n })\n // Remove headings markers, keep text\n .replace(/^#{1,6}\\s+/gm, \"\")\n // Remove horizontal rules\n .replace(/^(?:---|\\*\\*\\*|___)\\s*$/gm, \"\");\n result = stripInlineMarks(result)\n // Images: ![alt](url) → alt (must come before links)\n .replace(/!\\[([^\\]]*)\\]\\([^)]+\\)/g, \"$1\")\n // Links: [text](url) → text\n .replace(/\\[([^\\]]+)\\]\\([^)]+\\)/g, \"$1\")\n // Blockquote markers\n .replace(/^>\\s?/gm, \"\")\n // Table separator rows (|---|---|)\n .replace(/^\\|[-:\\s|]+\\|\\s*$/gm, \"\")\n // Table rows: | a | b | → a | b\n .replace(/^\\|\\s?/gm, \"\")\n .replace(/\\s?\\|$/gm, \"\")\n // Collapse multiple blank lines\n .replace(/\\n{3,}/g, \"\\n\\n\")\n .trim();\n\n // Restore inline code content\n result = result.replace(PLACEHOLDER_REGEX, (_match, index: string) => inlineCodeSnippets[Number(index)]);\n\n return result;\n}\n","import type { ReactNode } from \"react\";\nimport ReactMarkdown from \"react-markdown\";\nimport remarkBreaks from \"remark-breaks\";\nimport remarkGfm from \"remark-gfm\";\n\nimport { ScrollableTable } from \"../scrollable-table/scrollable-table\";\nimport type { BlockComponentProps, TextBlockRecord } from \"../types\";\nimport { sanitize } from \"./sanitize\";\nimport { TextBlockWrapper } from \"./text-block-styles\";\n\nconst markdownComponents = {\n table: ((tableProps: { node?: unknown; children?: ReactNode }) => (\n <ScrollableTable {...tableProps} />\n )) as React.ComponentType<unknown>,\n};\n\n/** Renders a TEXT block as Markdown with GFM (tables, strikethrough) and line breaks. */\nexport function TextBlock({ block }: BlockComponentProps<TextBlockRecord>): ReactNode {\n return (\n <TextBlockWrapper>\n <ReactMarkdown remarkPlugins={[remarkGfm, remarkBreaks]} components={markdownComponents}>\n {sanitize(block.payload.text)}\n </ReactMarkdown>\n </TextBlockWrapper>\n );\n}\n","import styled from \"styled-components\";\n\nexport const TableContainer = styled.div`\n position: relative;\n margin-block: 8px 16px;\n`;\n\nexport const TableScroll = styled.div`\n width: 100%;\n overflow-x: auto;\n overflow-y: hidden;\n`;\n","import { type ReactNode } from \"react\";\n\nimport { TableContainer, TableScroll } from \"./scrollable-table-styles\";\n\n/** Props for the ScrollableTable component. */\ninterface ScrollableTableProps {\n /** Unused node prop from react-markdown. */\n node?: unknown;\n /** Table children elements. */\n children?: ReactNode;\n}\n\n/** Renders a markdown table inside a horizontally scrollable wrapper. Read-only — actions live on artifact-tables. */\nexport function ScrollableTable({ node: _node, ...props }: ScrollableTableProps): ReactNode {\n return (\n <TableContainer>\n <TableScroll>\n <table {...props} />\n </TableScroll>\n </TableContainer>\n );\n}\n","// Strips io-server's `<!-- table-title: ... -->` HTML-comment metadata before rendering.\n// The marker is emitted by the server's CSV-export pipeline so the export button can label\n// the file; it has no place in the visual output. If the server-side format changes\n// (prefix, spacing, closing-tag style), this filter silently stops matching and the\n// raw HTML comment leaks into rendered Markdown — keep this contract aligned with io-server.\nexport function sanitize(text: string): string {\n return text\n .split(\"\\n\")\n .filter((line) => {\n const t = line.trim();\n return !(t.startsWith(\"<!-- table-title:\") && t.endsWith(\"-->\"));\n })\n .join(\"\\n\");\n}\n","import styled from \"styled-components\";\n\nimport { colors } from \"@/tokens/colors\";\nimport { typographyMixin, typographyTypes } from \"@/tokens/typography\";\n\nexport const TextBlockWrapper = styled.div`\n ${typographyMixin(typographyTypes.GEIST_BODY_S_REGULAR)};\n color: ${colors[\"brown-100\"]};\n word-break: break-word;\n\n & h1 {\n ${typographyMixin(typographyTypes.GEIST_HEADING_S_BOLD)};\n margin-block: 20px;\n }\n\n & h2 {\n ${typographyMixin(typographyTypes.GEIST_BODY_L_SEMI_BOLD)};\n margin-block: 12px;\n }\n\n & h3,\n & h4,\n & h5,\n & h6 {\n ${typographyMixin(typographyTypes.GEIST_BODY_M_SEMI_BOLD)};\n margin-block: 8px;\n }\n\n & strong {\n ${typographyMixin(typographyTypes.GEIST_BODY_S_SEMI_BOLD)};\n }\n\n & p {\n margin-block: 0px;\n }\n\n & p + p {\n margin-block: 8px 0px;\n }\n\n & ul,\n & ol {\n margin-block: 16px;\n padding-left: 20px;\n }\n\n & li + li {\n margin-block: 4px;\n }\n\n & ul {\n list-style-type: disc;\n }\n\n & ul ul {\n list-style-type: circle;\n }\n\n & ul ul ul {\n list-style-type: square;\n }\n\n & ol {\n list-style-type: decimal;\n }\n\n & ol ol {\n list-style-type: lower-alpha;\n }\n\n & ol ol ol {\n list-style-type: lower-roman;\n }\n\n & a {\n color: inherit;\n text-decoration-line: underline;\n text-decoration-style: dotted;\n text-decoration-thickness: auto;\n text-underline-offset: auto;\n text-underline-position: from-font;\n transition: color 75ms ease-in-out;\n\n &:hover {\n color: ${colors[\"blue-600\"]};\n }\n }\n\n & hr {\n border: none;\n border-top: 1px solid ${colors[\"brown-40\"]};\n margin-block: 20px;\n }\n\n & table {\n margin: 0;\n width: 100%;\n border-collapse: collapse;\n }\n\n & th,\n & td {\n border: none;\n border-bottom: 1px solid ${colors[\"brown-40\"]};\n text-align: left;\n }\n\n & th {\n ${typographyMixin(typographyTypes.GEIST_LABEL_CAPTION_MEDIUM)};\n padding: 8px;\n }\n\n & td {\n ${typographyMixin(typographyTypes.GEIST_BODY_XS_MEDIUM)};\n padding: 16px 8px;\n min-width: 150px;\n width: 1%;\n }\n\n & td strong {\n font-weight: inherit;\n font-size: inherit;\n line-height: inherit;\n }\n\n & blockquote {\n margin-block: 4px;\n margin-inline: 0px;\n padding-left: 16px;\n border-left: 4px solid ${colors[\"brown-40\"]};\n color: ${colors[\"brown-80\"]};\n }\n`;\n","import { markdownToPlainText } from \"@/utils/markdown-to-plain-text\";\nimport { TextBlock } from \"../text-block/text-block\";\nimport { blockTypes } from \"../types\";\nimport type { BlockService, TextBlockRecord } from \"../types\";\n\nexport const TextBlockService: BlockService<TextBlockRecord> = {\n type: blockTypes.TEXT,\n Component: TextBlock,\n /**\n * Converts the block's markdown source to plain text so the final clipboard value is clean\n * when pasted into tools that don't render markdown. Each block type owns its own clipboard\n * conversion so `useCopyToClipboard` can write text verbatim without a second pass.\n */\n toClipboardText: (block) => markdownToPlainText(block.payload.text),\n};\n","import { blockTypes, type BlockService, type BlockType } from \"../types\";\nimport { ArtifactRefBlockService } from \"./artifact-ref-block-service\";\nimport { TextBlockService } from \"./text-block-service\";\n\n// `BlockService<TBlock>` is invariant on `TBlock` via React's `ComponentType` (props are\n// contravariant). The concrete services overlap structurally with the base `BlockService` type\n// enough for a direct `as BlockService` cast. The registry dispatches by runtime discriminator —\n// `BlockServices[block.type]` always resolves to the matching concrete service — so the cast is sound.\nexport const BlockServices: Record<BlockType, BlockService> = {\n [blockTypes.TEXT]: TextBlockService as BlockService,\n [blockTypes.ARTIFACT_REF]: ArtifactRefBlockService as BlockService,\n};\n","import type { ReactNode } from \"react\";\n\nimport { BlockServices } from \"../block-services\";\nimport type { BlockRecord } from \"../types\";\n\n/** Props for the Block dispatcher. */\ninterface BlockProps {\n /** The block to render. Dispatched to the registered concrete component for its `type`. */\n block: BlockRecord;\n}\n\n/**\n * Dispatches a single block to its registered concrete component. Unknown types render as a no-op.\n *\n * Generic by design: per-block-type extras (e.g. localized artifact labels for `ARTIFACT_REF`)\n * live on the block's `payload`, not as sibling props here. That keeps the dispatcher's contract\n * shallow — third-party `BlockService` implementors only need to handle `{ block }` — and lets\n * the consumer materialise a fully-decorated block once (from the server payload + their i18n\n * layer) instead of threading sibling props through every dispatch site.\n */\nexport function Block({ block }: BlockProps): ReactNode {\n const service = BlockServices[block.type];\n if (!service) return null;\n const { Component } = service;\n return <Component block={block} />;\n}\n","import { BlockServices } from \"@/components/blocks/block-services\";\nimport type { ChatMessage } from \"../types\";\n\n/**\n * Aggregates the clipboard text for all blocks in a message.\n *\n * Iterates `message.blocks` in order, calls `BlockServices[block.type].toClipboardText(block)`\n * for each, and joins the results with `\"\\n\\n\"` so multi-block messages have a blank line between\n * each section (matching the visual gap rendered between blocks in the chat UI).\n *\n * Block-type-specific behaviour:\n * - `TEXT` blocks: markdown stripped to plain text via `markdownToPlainText`.\n * - `ARTIFACT_REF` blocks: delegates to the registered artifact component's `toClipboardText`\n * (e.g. the artifact title as plain text for table artifacts).\n * - Unknown block types: silently skipped (no registered service → no contribution to output).\n *\n * Falls back to `message.fallbackText` only when every block produces an empty string — this\n * guards against edge cases (empty message, all-unknown block types) while still preferring\n * the richer per-block text in the normal case.\n *\n * @param message The chat message whose blocks should be aggregated.\n * @returns A string suitable for writing to the system clipboard.\n */\nexport function copyMessageText(message: ChatMessage): string {\n const parts: string[] = [];\n\n for (const block of message.blocks) {\n const service = BlockServices[block.type];\n if (!service) continue;\n // `toClipboardText` is optional on the BlockService interface — external implementors may\n // omit it. Skip silently when absent (contributes \"\" to the aggregated output).\n if (!service.toClipboardText) continue;\n // The registry is keyed by BlockType and each service's toClipboardText is typed to its\n // concrete TBlock. The cast is safe because BlockServices[block.type] is always the matching\n // service for block.type — same soundness argument as the registry's own type erasure.\n const text = (service as { toClipboardText: (b: typeof block) => string }).toClipboardText(block);\n if (text) parts.push(text);\n }\n\n if (parts.length === 0) return message.fallbackText;\n return parts.join(\"\\n\\n\");\n}\n","import { useCallback } from \"react\";\nimport { IconCopy, IconCheck, IconThumbUp, IconThumbDown } from \"@tabler/icons-react\";\n\nimport { IconButton } from \"../../icon-button/icon-button\";\nimport { useChatContext } from \"../chat-context/chat-context\";\nimport { useCopyToClipboard } from \"../hooks/use-copy-to-clipboard\";\nimport { MESSAGE_ROLE } from \"../constants\";\nimport type { MessageRole } from \"../types\";\nimport { ActionsContainer, AnimatedAction } from \"./chat-message-actions-styles\";\n\ninterface ChatMessageActionsProps {\n className?: string;\n messageId: string;\n content: string;\n role: MessageRole;\n isHelpful?: boolean | null;\n}\n\nexport function ChatMessageActions({ className, messageId, content, role, isHelpful }: ChatMessageActionsProps) {\n const { onCopyMessage, onThumbUpClick, onThumbDownClick } = useChatContext();\n const { isCopied, copy } = useCopyToClipboard();\n\n const handleCopy = useCallback(() => {\n copy(content);\n onCopyMessage?.(messageId);\n }, [content, messageId, copy, onCopyMessage]);\n\n const handleThumbUp = useCallback(() => {\n onThumbUpClick?.(messageId, isHelpful === true ? null : true);\n }, [messageId, isHelpful, onThumbUpClick]);\n\n const handleThumbDown = useCallback(() => {\n onThumbDownClick?.(messageId, isHelpful === false ? null : false);\n }, [messageId, isHelpful, onThumbDownClick]);\n\n const isAssistant = role === MESSAGE_ROLE.ASSISTANT;\n const hasFeedback = isAssistant && (onThumbUpClick || onThumbDownClick);\n\n return (\n <ActionsContainer $role={role} className={className}>\n <IconButton\n icon={isCopied ? IconCheck : IconCopy}\n onClick={handleCopy}\n aria-label={isCopied ? \"Copied\" : \"Copy message\"}\n />\n\n {hasFeedback && (\n <AnimatedAction $visible={isHelpful !== false}>\n <IconButton\n icon={IconThumbUp}\n onClick={handleThumbUp}\n isSelected={isHelpful === true}\n aria-label=\"Good response\"\n />\n </AnimatedAction>\n )}\n\n {hasFeedback && (\n <AnimatedAction $visible={isHelpful !== true}>\n <IconButton\n icon={IconThumbDown}\n onClick={handleThumbDown}\n isSelected={isHelpful === false}\n aria-label=\"Bad response\"\n />\n </AnimatedAction>\n )}\n </ActionsContainer>\n );\n}\n","import { createContext, useContext } from \"react\";\nimport type { ChatContextValue } from \"../types\";\n\nexport const ChatContext = createContext<ChatContextValue | null>(null);\n\nexport function useChatContext(): ChatContextValue {\n const context = useContext(ChatContext);\n if (!context) {\n throw new Error(\"useChatContext must be used within a Chat.Root component\");\n }\n return context;\n}\n","import { useState, useCallback, useRef, useEffect } from \"react\";\n\nimport { copyToClipboard } from \"@/utils/clipboard\";\n\nconst RESET_DELAY_MS = 2000;\n\ninterface UseCopyToClipboardResult {\n isCopied: boolean;\n copy: (text: string) => void;\n}\n\nexport function useCopyToClipboard(): UseCopyToClipboardResult {\n const [isCopied, setIsCopied] = useState(false);\n const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n useEffect(() => {\n return () => {\n if (timeoutRef.current) clearTimeout(timeoutRef.current);\n };\n }, []);\n\n // Write text verbatim — callers (copyMessageText + each BlockService.toClipboardText) are\n // responsible for converting their own format to clipboard-ready plain text.\n const copy = useCallback((text: string) => {\n copyToClipboard(text, {\n onSuccess: () => {\n setIsCopied(true);\n if (timeoutRef.current) clearTimeout(timeoutRef.current);\n timeoutRef.current = setTimeout(() => {\n setIsCopied(false);\n timeoutRef.current = null;\n }, RESET_DELAY_MS);\n },\n });\n }, []);\n\n return { isCopied, copy };\n}\n","/** Options for the {@link copyToClipboard} function. */\ninterface CopyToClipboardOptions {\n /** Called after the text has been successfully written to the clipboard. */\n onSuccess?: () => void;\n /** Called if the clipboard write fails (e.g., permission denied). */\n onFailure?: (error: unknown) => void;\n}\n\n/**\n * Copies text to the system clipboard using the Clipboard API.\n * @param text - The string to copy.\n * @param options - Optional success/failure callbacks.\n */\nexport function copyToClipboard(text: string, options?: CopyToClipboardOptions): void {\n navigator.clipboard.writeText(text).then(\n () => options?.onSuccess?.(),\n (error) => options?.onFailure?.(error)\n );\n}\n","export const MESSAGE_ROLE = {\n USER: \"user\",\n ASSISTANT: \"assistant\",\n} as const;\n","import styled from \"styled-components\";\n\nimport { MESSAGE_ROLE } from \"../constants\";\nimport type { MessageRole } from \"../types\";\n\nconst FADE_DURATION_MS = 150;\nconst ANIMATE_DURATION_MS = 200;\n\nexport const ActionsContainer = styled.div<{ $role: MessageRole }>`\n display: flex;\n opacity: ${({ $role }) => ($role === MESSAGE_ROLE.ASSISTANT ? 1 : 0)};\n pointer-events: ${({ $role }) => ($role === MESSAGE_ROLE.ASSISTANT ? \"auto\" : \"none\")};\n transition: opacity ${FADE_DURATION_MS}ms ease;\n`;\n\nexport const AnimatedAction = styled.div<{ $visible: boolean }>`\n display: flex;\n overflow: hidden;\n max-width: ${({ $visible }) => ($visible ? \"40px\" : \"0\")};\n opacity: ${({ $visible }) => ($visible ? 1 : 0)};\n transition: max-width ${ANIMATE_DURATION_MS}ms ease, opacity ${ANIMATE_DURATION_MS}ms ease;\n`;\n","import styled from \"styled-components\";\n\nimport { colors } from \"../../../tokens/colors\";\nimport { typographyMixin, typographyTypes } from \"../../../tokens/typography\";\nimport { ActionsContainer } from \"../chat-message-actions/chat-message-actions-styles\";\nimport { MESSAGE_ROLE } from \"../constants\";\nimport type { MessageRole } from \"../types\";\n\nexport const MessageRow = styled.div<{ $role: MessageRole }>`\n display: flex;\n justify-content: ${({ $role }) => ($role === MESSAGE_ROLE.USER ? \"flex-end\" : \"flex-start\")};\n\n &:hover ${ActionsContainer} {\n opacity: 1;\n pointer-events: auto;\n }\n`;\n\nexport const MessageContainer = styled.div<{ $role: MessageRole }>`\n display: flex;\n flex-direction: column;\n align-items: ${({ $role }) => ($role === MESSAGE_ROLE.USER ? \"flex-end\" : \"flex-start\")};\n max-width: 90%;\n width: ${({ $role }) => ($role === MESSAGE_ROLE.USER ? \"auto\" : \"100%\")};\n gap: 12px;\n`;\n\nexport const MessageBubble = styled.div<{ $role: MessageRole }>`\n ${typographyMixin(typographyTypes.GEIST_BODY_S_REGULAR)};\n color: ${colors[\"brown-100\"]};\n background: ${({ $role }) => ($role === MESSAGE_ROLE.USER ? colors.white : \"transparent\")};\n padding: ${({ $role }) => ($role === MESSAGE_ROLE.USER ? \"12px 16px\" : \"0\")};\n border-radius: ${({ $role }) => ($role === MESSAGE_ROLE.USER ? \"16px\" : \"0\")};\n max-width: 100%;\n width: 100%;\n min-width: 0;\n /* Vertical rhythm between consecutive blocks — renderer-owned, not agent-emitted.\n * Industry standard (Claude, ChatGPT, Vercel AI Elements): the chat renderer applies\n * spacing via CSS; agents emit semantic blocks only. Value matches the MessageContainer\n * gap. TODO: replace 12px with a clay spacing token when a spacing scale is added. */\n display: flex;\n flex-direction: column;\n gap: 12px;\n`;\n"],"mappings":"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","names":["import_react","import_react","__iconNode","__iconNode","__iconNode","__iconNode","__iconNode","__iconNode","__iconNode","__iconNode","import_react","import_react","import_react","styled","import_jsx_runtime","Icon","import_styled_components","styled","import_jsx_runtime","IconButton","import_jsx_runtime","import_react","copyNodeAsPng","import_jsx_runtime","MAX_ERROR_MESSAGE_CHARS","truncateErrorMessage","import_jsx_runtime","DEFAULT_LABEL","import_react","React","import_jsx_runtime","import_jsx_runtime","downloadNodeAsPng","import_react","import_react","import_recharts","import_jsx_runtime","import_jsx_runtime","import_jsx_runtime","import_jsx_runtime","RechartsBarChart","import_jsx_runtime","import_react","import_recharts","import_jsx_runtime","RechartsLineChart","import_jsx_runtime","import_react","import_recharts","import_jsx_runtime","RechartsPieChart","import_jsx_runtime","import_jsx_runtime","Papa","import_react","import_jsx_runtime","import_react","React","import_jsx_runtime","import_react","import_jsx_runtime","import_jsx_runtime","TITLE_CLASSES","import_jsx_runtime","import_styled_components","styled","import_jsx_runtime","import_styled_components","styled","import_jsx_runtime","ReactMarkdown","remarkGfm","remarkBreaks","import_jsx_runtime","import_react","import_react","import_react","import_styled_components","styled","import_jsx_runtime","import_styled_components","styled","import_jsx_runtime"]}