@shapesos/clay 0.9.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/blocks/index.ts","../src/components/blocks/text-block/text-block.tsx","../src/components/blocks/block-context/block-context.tsx","../src/components/blocks/scrollable-table/scrollable-table-styles.ts","../src/tokens/colors.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/tokens/typography.ts","../src/components/blocks/types.ts","../src/components/blocks/block-services/text-block-service.ts","../src/components/blocks/block-services/index.ts","../src/components/blocks/block/block.tsx"],"sourcesContent":["export { Block } from \"./block\";\nexport { BlockContext, useBlockContext } from \"./block-context\";\nexport type { BlockContextValue } from \"./block-context\";\nexport { blockTypes } from \"./types\";\nexport type { BlockRecord, BlockType, TextBlockRecord, BlockComponentProps, BlockService } from \"./types\";\nexport { BlockServices } from \"./block-services\";\nexport { TextBlockService } from \"./block-services/text-block-service\";\n","import type { ReactNode } from \"react\";\nimport ReactMarkdown from \"react-markdown\";\nimport remarkBreaks from \"remark-breaks\";\nimport remarkGfm from \"remark-gfm\";\n\nimport { ScrollableTable, type TableCounter } from \"../scrollable-table/scrollable-table\";\nimport type { BlockComponentProps, TextBlockRecord } from \"../types\";\nimport { sanitize } from \"./sanitize\";\nimport { TextBlockWrapper } from \"./text-block-styles\";\n\n/** Renders a TEXT block as Markdown with GFM (tables, strikethrough) and line breaks. */\nexport function TextBlock({ block }: BlockComponentProps<TextBlockRecord>): ReactNode {\n const counter: TableCounter = { value: 0 };\n\n const markdownComponents = {\n table: ((tableProps: { node?: unknown; children?: ReactNode }) => (\n <ScrollableTable {...tableProps} counter={counter} />\n )) as React.ComponentType<unknown>,\n };\n\n return (\n <TextBlockWrapper>\n <ReactMarkdown remarkPlugins={[remarkGfm, remarkBreaks]} components={markdownComponents}>\n {sanitize(block.payload.text)}\n </ReactMarkdown>\n </TextBlockWrapper>\n );\n}\n","import { createContext, useContext, type ComponentType } from \"react\";\n\n/** Renderer-level concerns provided to descendants of any `Block`-rendering tree. */\nexport interface BlockContextValue {\n /** Optional component rendered next to each table in a TEXT block. Receives the zero-based table index. */\n TableActions?: ComponentType<{ tableIndex: number }>;\n}\n\nexport const BlockContext = createContext<BlockContextValue | null>(null);\n\n/** Returns the ambient block-rendering context. Returns an empty object outside a `<BlockContext.Provider>`. */\nexport function useBlockContext(): BlockContextValue {\n return useContext(BlockContext) ?? {};\n}\n","import styled from \"styled-components\";\n\nimport { colors } from \"@/tokens/colors\";\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\nexport const TableActionsWrapper = styled.div`\n display: flex;\n justify-content: flex-start;\n margin-block: 8px 0;\n`;\n\nexport const TableActionsDivider = styled.div`\n height: 1px;\n background: ${colors[\"brown-40\"]};\n margin-block: 8px 0;\n`;\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","import { type ReactNode } from \"react\";\n\nimport { useBlockContext } from \"../block-context/block-context\";\nimport { TableActionsWrapper, TableActionsDivider, TableContainer, TableScroll } from \"./scrollable-table-styles\";\n\n/** Mutable counter used to assign sequential table indices during a single render pass. */\nexport interface TableCounter {\n /** The current value; incremented each time a table component is rendered. */\n value: number;\n}\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 /** Mutable counter for tracking table indices. */\n counter?: TableCounter;\n}\n\n/** Renders a table inside a horizontally scrollable wrapper with optional actions below. */\nexport function ScrollableTable({ node: _node, counter, ...props }: ScrollableTableProps): ReactNode {\n const currentIndex = counter ? counter.value : 0;\n if (counter) {\n // eslint-disable-next-line react-hooks/immutability -- Counter is an intentionally mutable shared object for sequential table indexing\n counter.value += 1;\n }\n\n const { TableActions } = useBlockContext();\n\n return (\n <TableContainer>\n <TableScroll>\n <table {...props} />\n </TableScroll>\n {TableActions && (\n <>\n <TableActionsWrapper>\n <TableActions tableIndex={currentIndex} />\n </TableActionsWrapper>\n <TableActionsDivider />\n </>\n )}\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","/** 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 type { ComponentType } from \"react\";\n\n/** Block-type discriminator constants. Mirrors the wire shape from io-server / shapes-agent. */\nexport const blockTypes = {\n TEXT: \"TEXT\",\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/** Discriminated union of all supported content blocks. Additive — new types extend this union. */\nexport type BlockRecord = TextBlockRecord;\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 renderer. */\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","import { 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","import type { BlockService, BlockType } from \"../types\";\nimport { TextBlockService } from \"./text-block-service\";\n\nexport const BlockServices: Record<BlockType, BlockService> = {\n [TextBlockService.type]: TextBlockService,\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/** Dispatches a single block to its registered concrete component. Unknown types render as a no-op. */\nexport function Block({ block }: BlockProps): ReactNode {\n const service = BlockServices[block.type];\n if (!service) return null;\n const { Component } = service;\n return <Component block={block} />;\n}\n"],"mappings":"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","names":["styled","import_styled_components","styled","import_jsx_runtime","ReactMarkdown","remarkGfm","remarkBreaks","import_jsx_runtime"]}
@@ -0,0 +1,29 @@
1
+ import * as react from 'react';
2
+ import { ReactNode, ComponentType } from 'react';
3
+ import { a as BlockRecord, c as BlockType, b as BlockService, T as TextBlockRecord } from './types-Q9aqd9nq.cjs';
4
+ export { B as BlockComponentProps, d as blockTypes } from './types-Q9aqd9nq.cjs';
5
+
6
+ /** Props for the Block dispatcher. */
7
+ interface BlockProps {
8
+ /** The block to render. Dispatched to the registered concrete component for its `type`. */
9
+ block: BlockRecord;
10
+ }
11
+ /** Dispatches a single block to its registered concrete component. Unknown types render as a no-op. */
12
+ declare function Block({ block }: BlockProps): ReactNode;
13
+
14
+ /** Renderer-level concerns provided to descendants of any `Block`-rendering tree. */
15
+ interface BlockContextValue {
16
+ /** Optional component rendered next to each table in a TEXT block. Receives the zero-based table index. */
17
+ TableActions?: ComponentType<{
18
+ tableIndex: number;
19
+ }>;
20
+ }
21
+ declare const BlockContext: react.Context<BlockContextValue | null>;
22
+ /** Returns the ambient block-rendering context. Returns an empty object outside a `<BlockContext.Provider>`. */
23
+ declare function useBlockContext(): BlockContextValue;
24
+
25
+ declare const BlockServices: Record<BlockType, BlockService>;
26
+
27
+ declare const TextBlockService: BlockService<TextBlockRecord>;
28
+
29
+ export { Block, BlockContext, type BlockContextValue, BlockRecord, BlockService, BlockServices, BlockType, TextBlockRecord, TextBlockService, useBlockContext };
@@ -0,0 +1,29 @@
1
+ import * as react from 'react';
2
+ import { ReactNode, ComponentType } from 'react';
3
+ import { a as BlockRecord, c as BlockType, b as BlockService, T as TextBlockRecord } from './types-Q9aqd9nq.js';
4
+ export { B as BlockComponentProps, d as blockTypes } from './types-Q9aqd9nq.js';
5
+
6
+ /** Props for the Block dispatcher. */
7
+ interface BlockProps {
8
+ /** The block to render. Dispatched to the registered concrete component for its `type`. */
9
+ block: BlockRecord;
10
+ }
11
+ /** Dispatches a single block to its registered concrete component. Unknown types render as a no-op. */
12
+ declare function Block({ block }: BlockProps): ReactNode;
13
+
14
+ /** Renderer-level concerns provided to descendants of any `Block`-rendering tree. */
15
+ interface BlockContextValue {
16
+ /** Optional component rendered next to each table in a TEXT block. Receives the zero-based table index. */
17
+ TableActions?: ComponentType<{
18
+ tableIndex: number;
19
+ }>;
20
+ }
21
+ declare const BlockContext: react.Context<BlockContextValue | null>;
22
+ /** Returns the ambient block-rendering context. Returns an empty object outside a `<BlockContext.Provider>`. */
23
+ declare function useBlockContext(): BlockContextValue;
24
+
25
+ declare const BlockServices: Record<BlockType, BlockService>;
26
+
27
+ declare const TextBlockService: BlockService<TextBlockRecord>;
28
+
29
+ export { Block, BlockContext, type BlockContextValue, BlockRecord, BlockService, BlockServices, BlockType, TextBlockRecord, TextBlockService, useBlockContext };
package/dist/blocks.js ADDED
@@ -0,0 +1,21 @@
1
+ import "./chunk-OKPNST44.js";
2
+ import {
3
+ Block,
4
+ BlockContext,
5
+ BlockServices,
6
+ TextBlockService,
7
+ blockTypes,
8
+ useBlockContext
9
+ } from "./chunk-R3BGPOAM.js";
10
+ import "./chunk-SV24ONND.js";
11
+ import "./chunk-JF3P66JF.js";
12
+ import "./chunk-5WRI5ZAA.js";
13
+ export {
14
+ Block,
15
+ BlockContext,
16
+ BlockServices,
17
+ TextBlockService,
18
+ blockTypes,
19
+ useBlockContext
20
+ };
21
+ //# sourceMappingURL=blocks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
package/dist/chat.cjs CHANGED
@@ -38,26 +38,22 @@ __export(chat_exports, {
38
38
  });
39
39
  module.exports = __toCommonJS(chat_exports);
40
40
 
41
- // src/components/chat/chat-message-content/chat-message-content.tsx
41
+ // src/components/chat/chat-message/chat-message.tsx
42
+ var import_react7 = require("react");
43
+
44
+ // src/components/blocks/text-block/text-block.tsx
42
45
  var import_react_markdown = __toESM(require("react-markdown"), 1);
43
46
  var import_remark_breaks = __toESM(require("remark-breaks"), 1);
44
47
  var import_remark_gfm = __toESM(require("remark-gfm"), 1);
45
48
 
46
- // src/components/chat/scrollable-table/scrollable-table.tsx
47
- var import_react2 = require("react");
48
-
49
- // src/components/chat/chat-context/chat-context.ts
49
+ // src/components/blocks/block-context/block-context.tsx
50
50
  var import_react = require("react");
51
- var ChatContext = (0, import_react.createContext)(null);
52
- function useChatContext() {
53
- const context = (0, import_react.useContext)(ChatContext);
54
- if (!context) {
55
- throw new Error("useChatContext must be used within a Chat.Root component");
56
- }
57
- return context;
51
+ var BlockContext = (0, import_react.createContext)(null);
52
+ function useBlockContext() {
53
+ return (0, import_react.useContext)(BlockContext) ?? {};
58
54
  }
59
55
 
60
- // src/components/chat/scrollable-table/scrollable-table-styles.ts
56
+ // src/components/blocks/scrollable-table/scrollable-table-styles.ts
61
57
  var import_styled_components = __toESM(require("styled-components"), 1);
62
58
 
63
59
  // src/tokens/colors.ts
@@ -148,7 +144,7 @@ var colors = {
148
144
  "red-600": "#DC2626"
149
145
  };
150
146
 
151
- // src/components/chat/scrollable-table/scrollable-table-styles.ts
147
+ // src/components/blocks/scrollable-table/scrollable-table-styles.ts
152
148
  var TableContainer = import_styled_components.default.div`
153
149
  position: relative;
154
150
  margin-block: 8px 16px;
@@ -169,25 +165,32 @@ var TableActionsDivider = import_styled_components.default.div`
169
165
  margin-block: 8px 0;
170
166
  `;
171
167
 
172
- // src/components/chat/scrollable-table/scrollable-table.tsx
168
+ // src/components/blocks/scrollable-table/scrollable-table.tsx
173
169
  var import_jsx_runtime = require("react/jsx-runtime");
174
- function ScrollableTable({ node: _node, messageId, counter, ...props }) {
170
+ function ScrollableTable({ node: _node, counter, ...props }) {
175
171
  const currentIndex = counter ? counter.value : 0;
176
172
  if (counter) {
177
173
  counter.value += 1;
178
174
  }
179
- const context = (0, import_react2.useContext)(ChatContext);
180
- const TableActions = context?.TableActions;
175
+ const { TableActions } = useBlockContext();
181
176
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(TableContainer, { children: [
182
177
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableScroll, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("table", { ...props }) }),
183
- TableActions && messageId && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
184
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableActionsWrapper, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableActions, { messageId, tableIndex: currentIndex }) }),
178
+ TableActions && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
179
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableActionsWrapper, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableActions, { tableIndex: currentIndex }) }),
185
180
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableActionsDivider, {})
186
181
  ] })
187
182
  ] });
188
183
  }
189
184
 
190
- // src/components/chat/chat-message-content/chat-message-content-styles.ts
185
+ // src/components/blocks/text-block/sanitize.ts
186
+ function sanitize(text) {
187
+ return text.split("\n").filter((line) => {
188
+ const t = line.trim();
189
+ return !(t.startsWith("<!-- table-title:") && t.endsWith("-->"));
190
+ }).join("\n");
191
+ }
192
+
193
+ // src/components/blocks/text-block/text-block-styles.ts
191
194
  var import_styled_components2 = __toESM(require("styled-components"), 1);
192
195
 
193
196
  // src/tokens/typography.ts
@@ -485,8 +488,8 @@ function typographyMixin(type) {
485
488
  ].join(";\n ");
486
489
  }
487
490
 
488
- // src/components/chat/chat-message-content/chat-message-content-styles.ts
489
- var ContentWrapper = import_styled_components2.default.div`
491
+ // src/components/blocks/text-block/text-block-styles.ts
492
+ var TextBlockWrapper = import_styled_components2.default.div`
490
493
  ${typographyMixin(typographyTypes.GEIST_BODY_S_REGULAR)};
491
494
  color: ${colors["brown-100"]};
492
495
  word-break: break-word;
@@ -603,18 +606,50 @@ var ContentWrapper = import_styled_components2.default.div`
603
606
  }
604
607
  `;
605
608
 
606
- // src/components/chat/chat-message-content/chat-message-content.tsx
609
+ // src/components/blocks/text-block/text-block.tsx
607
610
  var import_jsx_runtime2 = require("react/jsx-runtime");
608
- function ChatMessageContent({ content, messageId }) {
611
+ function TextBlock({ block }) {
609
612
  const counter = { value: 0 };
610
- const sanitizedContent = content.split("\n").filter((line) => {
611
- const t = line.trim();
612
- return !(t.startsWith("<!-- table-title:") && t.endsWith("-->"));
613
- }).join("\n");
614
613
  const markdownComponents = {
615
- table: ((tableProps) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ScrollableTable, { ...tableProps, messageId, counter }))
614
+ table: ((tableProps) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ScrollableTable, { ...tableProps, counter }))
616
615
  };
617
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ContentWrapper, { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_markdown.default, { remarkPlugins: [import_remark_gfm.default, import_remark_breaks.default], components: markdownComponents, children: sanitizedContent }) });
616
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(TextBlockWrapper, { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_markdown.default, { remarkPlugins: [import_remark_gfm.default, import_remark_breaks.default], components: markdownComponents, children: sanitize(block.payload.text) }) });
617
+ }
618
+
619
+ // src/components/blocks/types.ts
620
+ var blockTypes = {
621
+ TEXT: "TEXT"
622
+ };
623
+
624
+ // src/components/blocks/block-services/text-block-service.ts
625
+ var TextBlockService = {
626
+ type: blockTypes.TEXT,
627
+ Component: TextBlock
628
+ };
629
+
630
+ // src/components/blocks/block-services/index.ts
631
+ var BlockServices = {
632
+ [TextBlockService.type]: TextBlockService
633
+ };
634
+
635
+ // src/components/blocks/block/block.tsx
636
+ var import_jsx_runtime3 = require("react/jsx-runtime");
637
+ function Block({ block }) {
638
+ const service = BlockServices[block.type];
639
+ if (!service) return null;
640
+ const { Component } = service;
641
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Component, { block });
642
+ }
643
+
644
+ // src/components/chat/chat-context/chat-context.ts
645
+ var import_react2 = require("react");
646
+ var ChatContext = (0, import_react2.createContext)(null);
647
+ function useChatContext() {
648
+ const context = (0, import_react2.useContext)(ChatContext);
649
+ if (!context) {
650
+ throw new Error("useChatContext must be used within a Chat.Root component");
651
+ }
652
+ return context;
618
653
  }
619
654
 
620
655
  // src/components/chat/chat-message-actions/chat-message-actions.tsx
@@ -718,10 +753,10 @@ var IconWrapper = import_styled_components3.default.span`
718
753
  `;
719
754
 
720
755
  // src/components/icon/icon.tsx
721
- var import_jsx_runtime3 = require("react/jsx-runtime");
756
+ var import_jsx_runtime4 = require("react/jsx-runtime");
722
757
  var DEFAULT_SIZE = 16;
723
758
  var Icon = (0, import_react4.forwardRef)(function Icon2({ icon: IconComponent, size = DEFAULT_SIZE, color, className, "aria-label": ariaLabel }, ref) {
724
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
759
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
725
760
  IconWrapper,
726
761
  {
727
762
  ref,
@@ -729,7 +764,7 @@ var Icon = (0, import_react4.forwardRef)(function Icon2({ icon: IconComponent, s
729
764
  $color: color,
730
765
  "aria-label": ariaLabel,
731
766
  role: ariaLabel ? "img" : void 0,
732
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(IconComponent, { width: size, height: size, strokeWidth: getStrokeWidth(size) })
767
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(IconComponent, { width: size, height: size, strokeWidth: getStrokeWidth(size) })
733
768
  }
734
769
  );
735
770
  });
@@ -778,7 +813,7 @@ var Button = import_styled_components4.default.button`
778
813
  `;
779
814
 
780
815
  // src/components/icon-button/icon-button.tsx
781
- var import_jsx_runtime4 = require("react/jsx-runtime");
816
+ var import_jsx_runtime5 = require("react/jsx-runtime");
782
817
  var ICON_SIZE_BY_BUTTON_SIZE = {
783
818
  small: 16,
784
819
  medium: 20
@@ -795,7 +830,7 @@ function IconButton({
795
830
  as,
796
831
  "aria-label": ariaLabel
797
832
  }) {
798
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
833
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
799
834
  Button,
800
835
  {
801
836
  className,
@@ -807,7 +842,7 @@ function IconButton({
807
842
  disabled,
808
843
  onClick: disabled ? void 0 : onClick,
809
844
  "aria-label": ariaLabel,
810
- children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Icon, { icon, size: ICON_SIZE_BY_BUTTON_SIZE[size] })
845
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Icon, { icon, size: ICON_SIZE_BY_BUTTON_SIZE[size] })
811
846
  }
812
847
  );
813
848
  }
@@ -892,7 +927,7 @@ var AnimatedAction = import_styled_components5.default.div`
892
927
  `;
893
928
 
894
929
  // src/components/chat/chat-message-actions/chat-message-actions.tsx
895
- var import_jsx_runtime5 = require("react/jsx-runtime");
930
+ var import_jsx_runtime6 = require("react/jsx-runtime");
896
931
  function ChatMessageActions({ className, messageId, content, role, isHelpful }) {
897
932
  const { onCopyMessage, onThumbUpClick, onThumbDownClick } = useChatContext();
898
933
  const { isCopied, copy } = useCopyToClipboard();
@@ -908,8 +943,8 @@ function ChatMessageActions({ className, messageId, content, role, isHelpful })
908
943
  }, [messageId, isHelpful, onThumbDownClick]);
909
944
  const isAssistant = role === MESSAGE_ROLE.ASSISTANT;
910
945
  const hasFeedback = isAssistant && (onThumbUpClick || onThumbDownClick);
911
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(ActionsContainer, { $role: role, className, children: [
912
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
946
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(ActionsContainer, { $role: role, className, children: [
947
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
913
948
  IconButton,
914
949
  {
915
950
  icon: isCopied ? IconCheck : IconCopy,
@@ -917,7 +952,7 @@ function ChatMessageActions({ className, messageId, content, role, isHelpful })
917
952
  "aria-label": isCopied ? "Copied" : "Copy message"
918
953
  }
919
954
  ),
920
- hasFeedback && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(AnimatedAction, { $visible: isHelpful !== false, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
955
+ hasFeedback && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(AnimatedAction, { $visible: isHelpful !== false, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
921
956
  IconButton,
922
957
  {
923
958
  icon: IconThumbUp,
@@ -926,7 +961,7 @@ function ChatMessageActions({ className, messageId, content, role, isHelpful })
926
961
  "aria-label": "Good response"
927
962
  }
928
963
  ) }),
929
- hasFeedback && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(AnimatedAction, { $visible: isHelpful !== true, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
964
+ hasFeedback && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(AnimatedAction, { $visible: isHelpful !== true, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
930
965
  IconButton,
931
966
  {
932
967
  icon: IconThumbDown,
@@ -968,15 +1003,22 @@ var MessageBubble = import_styled_components6.default.div`
968
1003
  `;
969
1004
 
970
1005
  // src/components/chat/chat-message/chat-message.tsx
971
- var import_jsx_runtime6 = require("react/jsx-runtime");
1006
+ var import_jsx_runtime7 = require("react/jsx-runtime");
972
1007
  function ChatMessage({ message }) {
973
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MessageRow, { $role: message.role, role: "article", "aria-label": `${message.role} message`, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(MessageContainer, { $role: message.role, children: [
974
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MessageBubble, { $role: message.role, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ChatMessageContent, { content: message.content, messageId: message.id }) }),
975
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1008
+ const { TableActions: ChatTableActions } = useChatContext();
1009
+ const blockContextValue = (0, import_react7.useMemo)(
1010
+ () => ({
1011
+ TableActions: ChatTableActions ? ({ tableIndex }) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ChatTableActions, { messageId: message.id, tableIndex }) : void 0
1012
+ }),
1013
+ [ChatTableActions, message.id]
1014
+ );
1015
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(MessageRow, { $role: message.role, role: "article", "aria-label": `${message.role} message`, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(MessageContainer, { $role: message.role, children: [
1016
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(MessageBubble, { $role: message.role, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(BlockContext.Provider, { value: blockContextValue, children: message.blocks.map((block, index) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Block, { block }, index)) }) }),
1017
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
976
1018
  ChatMessageActions,
977
1019
  {
978
1020
  messageId: message.id,
979
- content: message.content,
1021
+ content: message.fallbackText,
980
1022
  role: message.role,
981
1023
  isHelpful: message.isHelpful
982
1024
  }