reachat 2.1.0-alpha.18 → 2.1.0-alpha.19

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.
@@ -3,7 +3,7 @@ import { AnimatePresence, motion } from "motion/react";
3
3
  import { IconButton } from "reablocks";
4
4
  import * as React from "react";
5
5
  import { useContext, useState, useRef, useEffect } from "react";
6
- import { C as ChatContext, a as SvgCopy } from "./index-D20wtImV.js";
6
+ import { C as ChatContext, a as SvgCopy } from "./index-xZVXUqlo.js";
7
7
  const SvgDownload = (props) => /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", width: "24px", height: "24px", ...props }, /* @__PURE__ */ React.createElement("path", { d: "M 11 2 C 10.448 2 10 2.448 10 3 L 10 11 L 6.5 11 A 0.5 0.5 0 0 0 6 11.5 A 0.5 0.5 0 0 0 6.1464844 11.853516 A 0.5 0.5 0 0 0 6.1777344 11.882812 L 11.283203 16.697266 L 11.316406 16.728516 A 1 1 0 0 0 12 17 A 1 1 0 0 0 12.683594 16.728516 L 12.697266 16.716797 A 1 1 0 0 0 12.707031 16.705078 L 17.810547 11.892578 A 0.5 0.5 0 0 0 17.839844 11.865234 L 17.847656 11.859375 A 0.5 0.5 0 0 0 17.853516 11.853516 A 0.5 0.5 0 0 0 18 11.5 A 0.5 0.5 0 0 0 17.5 11 L 14 11 L 14 3 C 14 2.448 13.552 2 13 2 L 12 2 L 11 2 z M 3 20 A 1.0001 1.0001 0 1 0 3 22 L 21 22 A 1.0001 1.0001 0 1 0 21 20 L 3 20 z", fill: "currentColor" }));
8
8
  const sanitizeSVGCell = (cell) => {
9
9
  const trimmed = cell.trim();
@@ -139,4 +139,4 @@ const CSVFileRenderer = ({ name, url, fileIcon }) => {
139
139
  export {
140
140
  CSVFileRenderer as default
141
141
  };
142
- //# sourceMappingURL=CSVFileRenderer-C68XRlb0.js.map
142
+ //# sourceMappingURL=CSVFileRenderer-DTl0pUQa.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"CSVFileRenderer-C68XRlb0.js","sources":["../src/assets/download.svg?react","../src/utils/sanitize.ts","../src/utils/parseCSV.ts","../src/SessionMessages/SessionMessage/MessageFile/renderers/CSVFileRenderer.tsx"],"sourcesContent":["import * as React from \"react\";\nconst SvgDownload = (props) => /* @__PURE__ */ React.createElement(\"svg\", { xmlns: \"http://www.w3.org/2000/svg\", viewBox: \"0 0 24 24\", width: \"24px\", height: \"24px\", ...props }, /* @__PURE__ */ React.createElement(\"path\", { d: \"M 11 2 C 10.448 2 10 2.448 10 3 L 10 11 L 6.5 11 A 0.5 0.5 0 0 0 6 11.5 A 0.5 0.5 0 0 0 6.1464844 11.853516 A 0.5 0.5 0 0 0 6.1777344 11.882812 L 11.283203 16.697266 L 11.316406 16.728516 A 1 1 0 0 0 12 17 A 1 1 0 0 0 12.683594 16.728516 L 12.697266 16.716797 A 1 1 0 0 0 12.707031 16.705078 L 17.810547 11.892578 A 0.5 0.5 0 0 0 17.839844 11.865234 L 17.847656 11.859375 A 0.5 0.5 0 0 0 17.853516 11.853516 A 0.5 0.5 0 0 0 18 11.5 A 0.5 0.5 0 0 0 17.5 11 L 14 11 L 14 3 C 14 2.448 13.552 2 13 2 L 12 2 L 11 2 z M 3 20 A 1.0001 1.0001 0 1 0 3 22 L 21 22 A 1.0001 1.0001 0 1 0 21 20 L 3 20 z\", fill: \"currentColor\" }));\nexport default SvgDownload;\n","/**\n * Sanitizes cell content to prevent CSV injection and other potential vulnerabilities.\n * Based on the documentation of OWASP for CSV Injection\n * https://owasp.org/www-community/attacks/CSV_Injection\n * @param cell The cell content to sanitize.\n * @returns The sanitized cell content.\n */\nexport const sanitizeSVGCell = (cell: string): string => {\n const trimmed = cell.trim();\n // Escape double quotes by doubling them\n const escaped = trimmed.replace(/\"/g, '\"\"');\n // Add single quote prefix only for potentially dangerous content\n const prefix = /^[=+\\-@]/.test(trimmed) ? \"'\" : '';\n // Only wrap in quotes if the content contains special characters\n const needsQuotes = /[\",\\n\\r]/.test(escaped) || prefix;\n\n return needsQuotes ? `\"${prefix}${escaped}\"` : escaped;\n};\n","import { sanitizeSVGCell } from './sanitize';\n\n/**\n * Parses a CSV string from a local file and returns an array of rows.\n * Sanitizes cell data to prevent injection attacks.\n * @param csvString The raw CSV string content to parse.\n * @returns The parsed CSV data as a 2D array of strings.\n */\nexport const parseCSV = (csvString: string): string[][] => {\n try {\n const rows = csvString.split('\\n');\n return rows.map(row => row.split(',').map(cell => sanitizeSVGCell(cell)));\n } catch (error) {\n console.error('Error parsing CSV:', error);\n throw new Error('Failed to parse CSV file.');\n }\n};\n","import { AnimatePresence, motion } from 'motion/react';\nimport { IconButton } from 'reablocks';\nimport type { FC, ReactElement } from 'react';\nimport { useContext, useEffect, useRef, useState } from 'react';\n\nimport PlaceholderIcon from '@/assets/copy.svg?react';\nimport DownloadIcon from '@/assets/download.svg?react';\nimport { ChatContext } from '@/ChatContext';\nimport { parseCSV } from '@/utils/parseCSV';\n\ninterface CSVFileRendererProps {\n /**\n * Name of the file.\n */\n name?: string;\n\n /**\n * URL of the file.\n */\n url: string;\n\n /**\n * Icon to for file type.\n */\n fileIcon?: ReactElement;\n}\n\n/**\n * Renderer for CSV files that fetches and displays a snippet of the file data.\n */\nconst CSVFileRenderer: FC<CSVFileRendererProps> = ({ name, url, fileIcon }) => {\n const { theme } = useContext(ChatContext);\n const [isLoading, setIsLoading] = useState<boolean>(true);\n const [csvData, setCsvData] = useState<string[][]>([]);\n const [error, setError] = useState<string | null>(null);\n const [isModalOpen, setIsModalOpen] = useState(false);\n const modalRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n const fetchCsvData = async () => {\n try {\n setIsLoading(true);\n const response = await fetch(url);\n const data = parseCSV(await response.text());\n setCsvData(data);\n } catch {\n setError('Failed to load CSV file.');\n } finally {\n setIsLoading(false);\n }\n };\n\n fetchCsvData();\n }, [url]);\n\n const toggleModal = () => {\n setIsModalOpen(prev => !prev);\n };\n\n const handleClickOutside = (event: MouseEvent) => {\n if (modalRef.current && !modalRef.current.contains(event.target as Node)) {\n setIsModalOpen(false);\n }\n };\n\n useEffect(() => {\n if (isModalOpen) {\n document.addEventListener('mousedown', handleClickOutside);\n } else {\n document.removeEventListener('mousedown', handleClickOutside);\n }\n return () => {\n document.removeEventListener('mousedown', handleClickOutside);\n };\n }, [isModalOpen]);\n\n const downloadCSV = () => {\n if (csvData.length === 0) return;\n\n const csvContent = csvData.map(row => row.join(',')).join('\\n');\n const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });\n const url = URL.createObjectURL(blob);\n const link = document.createElement('a');\n link.href = url;\n link.setAttribute('download', `${name || 'data'}`);\n document.body.appendChild(link);\n link.click();\n document.body.removeChild(link);\n };\n\n const renderTable = (data: string[][], maxRows?: number) => (\n <motion.table\n layout\n className=\"w-full\"\n transition={{ type: 'spring', stiffness: 100, damping: 20 }}\n >\n <thead className=\"sticky top-0 bg-gray-200 dark:bg-gray-800 z-10\">\n <tr>\n <th className=\"py-4 px-6\">#</th>\n {data[0].map((header, index) => (\n <th key={`header-${index}`} className=\"py-4 px-6\">\n {header}\n </th>\n ))}\n </tr>\n </thead>\n <tbody>\n {data.slice(1, maxRows).map((row, rowIndex) => (\n <tr\n key={`row-${rowIndex}`}\n className=\"border-b border-panel-accent light:border-gray-700 hover:bg-panel-accent hover:light:bg-gray-700/40 transition-colors text-base\"\n >\n <td className=\"py-4 px-6\">{rowIndex + 1}</td>\n {row.map((cell, cellIndex) => (\n <td key={`cell-${rowIndex}-${cellIndex}`} className=\"py-4 px-6\">\n {cell}\n </td>\n ))}\n </tr>\n ))}\n </tbody>\n </motion.table>\n );\n\n return (\n <div className={theme.messages.message.csvPreview.base}>\n <div className={theme.messages.message.csvPreview.header.base}>\n <div className={theme.messages.message.csvPreview.header.icon}>\n {fileIcon}\n {name && <figcaption className=\"ml-1\">{name}</figcaption>}\n </div>\n <div className={theme.messages.message.csvPreview.header.actions}>\n <IconButton size=\"medium\" variant=\"text\" onClick={downloadCSV}>\n <DownloadIcon />\n </IconButton>\n <IconButton size=\"medium\" variant=\"text\" onClick={toggleModal}>\n <PlaceholderIcon />\n </IconButton>\n </div>\n </div>\n\n {error && <div className=\"error-message\">{error}</div>}\n\n {isLoading && !csvData && (\n <div className=\"text-text-secondary\">Loading...</div>\n )}\n\n <div className={theme.messages.message.csvPreview.tableContainer}>\n {!error && csvData.length > 0 && renderTable(csvData, 6)}\n </div>\n\n <AnimatePresence>\n {isModalOpen && (\n <motion.div\n className={theme.messages.message.csvPreview.dialog.base}\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={{ duration: 0.3 }}\n >\n <motion.div\n ref={modalRef}\n className={theme.messages.message.csvPreview.dialog.container}\n initial={{ scale: 0.8 }}\n animate={{ scale: 1 }}\n exit={{ scale: 0.8 }}\n transition={{ duration: 0.3 }}\n >\n {!error && csvData.length > 0 && renderTable(csvData)}\n </motion.div>\n </motion.div>\n )}\n </AnimatePresence>\n </div>\n );\n};\n\nexport default CSVFileRenderer;\n"],"names":["url","DownloadIcon","PlaceholderIcon"],"mappings":"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"}
1
+ {"version":3,"file":"CSVFileRenderer-DTl0pUQa.js","sources":["../src/assets/download.svg?react","../src/utils/sanitize.ts","../src/utils/parseCSV.ts","../src/SessionMessages/SessionMessage/MessageFile/renderers/CSVFileRenderer.tsx"],"sourcesContent":["import * as React from \"react\";\nconst SvgDownload = (props) => /* @__PURE__ */ React.createElement(\"svg\", { xmlns: \"http://www.w3.org/2000/svg\", viewBox: \"0 0 24 24\", width: \"24px\", height: \"24px\", ...props }, /* @__PURE__ */ React.createElement(\"path\", { d: \"M 11 2 C 10.448 2 10 2.448 10 3 L 10 11 L 6.5 11 A 0.5 0.5 0 0 0 6 11.5 A 0.5 0.5 0 0 0 6.1464844 11.853516 A 0.5 0.5 0 0 0 6.1777344 11.882812 L 11.283203 16.697266 L 11.316406 16.728516 A 1 1 0 0 0 12 17 A 1 1 0 0 0 12.683594 16.728516 L 12.697266 16.716797 A 1 1 0 0 0 12.707031 16.705078 L 17.810547 11.892578 A 0.5 0.5 0 0 0 17.839844 11.865234 L 17.847656 11.859375 A 0.5 0.5 0 0 0 17.853516 11.853516 A 0.5 0.5 0 0 0 18 11.5 A 0.5 0.5 0 0 0 17.5 11 L 14 11 L 14 3 C 14 2.448 13.552 2 13 2 L 12 2 L 11 2 z M 3 20 A 1.0001 1.0001 0 1 0 3 22 L 21 22 A 1.0001 1.0001 0 1 0 21 20 L 3 20 z\", fill: \"currentColor\" }));\nexport default SvgDownload;\n","/**\n * Sanitizes cell content to prevent CSV injection and other potential vulnerabilities.\n * Based on the documentation of OWASP for CSV Injection\n * https://owasp.org/www-community/attacks/CSV_Injection\n * @param cell The cell content to sanitize.\n * @returns The sanitized cell content.\n */\nexport const sanitizeSVGCell = (cell: string): string => {\n const trimmed = cell.trim();\n // Escape double quotes by doubling them\n const escaped = trimmed.replace(/\"/g, '\"\"');\n // Add single quote prefix only for potentially dangerous content\n const prefix = /^[=+\\-@]/.test(trimmed) ? \"'\" : '';\n // Only wrap in quotes if the content contains special characters\n const needsQuotes = /[\",\\n\\r]/.test(escaped) || prefix;\n\n return needsQuotes ? `\"${prefix}${escaped}\"` : escaped;\n};\n","import { sanitizeSVGCell } from './sanitize';\n\n/**\n * Parses a CSV string from a local file and returns an array of rows.\n * Sanitizes cell data to prevent injection attacks.\n * @param csvString The raw CSV string content to parse.\n * @returns The parsed CSV data as a 2D array of strings.\n */\nexport const parseCSV = (csvString: string): string[][] => {\n try {\n const rows = csvString.split('\\n');\n return rows.map(row => row.split(',').map(cell => sanitizeSVGCell(cell)));\n } catch (error) {\n console.error('Error parsing CSV:', error);\n throw new Error('Failed to parse CSV file.');\n }\n};\n","import { AnimatePresence, motion } from 'motion/react';\nimport { IconButton } from 'reablocks';\nimport type { FC, ReactElement } from 'react';\nimport { useContext, useEffect, useRef, useState } from 'react';\n\nimport PlaceholderIcon from '@/assets/copy.svg?react';\nimport DownloadIcon from '@/assets/download.svg?react';\nimport { ChatContext } from '@/ChatContext';\nimport { parseCSV } from '@/utils/parseCSV';\n\ninterface CSVFileRendererProps {\n /**\n * Name of the file.\n */\n name?: string;\n\n /**\n * URL of the file.\n */\n url: string;\n\n /**\n * Icon to for file type.\n */\n fileIcon?: ReactElement;\n}\n\n/**\n * Renderer for CSV files that fetches and displays a snippet of the file data.\n */\nconst CSVFileRenderer: FC<CSVFileRendererProps> = ({ name, url, fileIcon }) => {\n const { theme } = useContext(ChatContext);\n const [isLoading, setIsLoading] = useState<boolean>(true);\n const [csvData, setCsvData] = useState<string[][]>([]);\n const [error, setError] = useState<string | null>(null);\n const [isModalOpen, setIsModalOpen] = useState(false);\n const modalRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n const fetchCsvData = async () => {\n try {\n setIsLoading(true);\n const response = await fetch(url);\n const data = parseCSV(await response.text());\n setCsvData(data);\n } catch {\n setError('Failed to load CSV file.');\n } finally {\n setIsLoading(false);\n }\n };\n\n fetchCsvData();\n }, [url]);\n\n const toggleModal = () => {\n setIsModalOpen(prev => !prev);\n };\n\n const handleClickOutside = (event: MouseEvent) => {\n if (modalRef.current && !modalRef.current.contains(event.target as Node)) {\n setIsModalOpen(false);\n }\n };\n\n useEffect(() => {\n if (isModalOpen) {\n document.addEventListener('mousedown', handleClickOutside);\n } else {\n document.removeEventListener('mousedown', handleClickOutside);\n }\n return () => {\n document.removeEventListener('mousedown', handleClickOutside);\n };\n }, [isModalOpen]);\n\n const downloadCSV = () => {\n if (csvData.length === 0) return;\n\n const csvContent = csvData.map(row => row.join(',')).join('\\n');\n const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });\n const url = URL.createObjectURL(blob);\n const link = document.createElement('a');\n link.href = url;\n link.setAttribute('download', `${name || 'data'}`);\n document.body.appendChild(link);\n link.click();\n document.body.removeChild(link);\n };\n\n const renderTable = (data: string[][], maxRows?: number) => (\n <motion.table\n layout\n className=\"w-full\"\n transition={{ type: 'spring', stiffness: 100, damping: 20 }}\n >\n <thead className=\"sticky top-0 bg-gray-200 dark:bg-gray-800 z-10\">\n <tr>\n <th className=\"py-4 px-6\">#</th>\n {data[0].map((header, index) => (\n <th key={`header-${index}`} className=\"py-4 px-6\">\n {header}\n </th>\n ))}\n </tr>\n </thead>\n <tbody>\n {data.slice(1, maxRows).map((row, rowIndex) => (\n <tr\n key={`row-${rowIndex}`}\n className=\"border-b border-panel-accent light:border-gray-700 hover:bg-panel-accent hover:light:bg-gray-700/40 transition-colors text-base\"\n >\n <td className=\"py-4 px-6\">{rowIndex + 1}</td>\n {row.map((cell, cellIndex) => (\n <td key={`cell-${rowIndex}-${cellIndex}`} className=\"py-4 px-6\">\n {cell}\n </td>\n ))}\n </tr>\n ))}\n </tbody>\n </motion.table>\n );\n\n return (\n <div className={theme.messages.message.csvPreview.base}>\n <div className={theme.messages.message.csvPreview.header.base}>\n <div className={theme.messages.message.csvPreview.header.icon}>\n {fileIcon}\n {name && <figcaption className=\"ml-1\">{name}</figcaption>}\n </div>\n <div className={theme.messages.message.csvPreview.header.actions}>\n <IconButton size=\"medium\" variant=\"text\" onClick={downloadCSV}>\n <DownloadIcon />\n </IconButton>\n <IconButton size=\"medium\" variant=\"text\" onClick={toggleModal}>\n <PlaceholderIcon />\n </IconButton>\n </div>\n </div>\n\n {error && <div className=\"error-message\">{error}</div>}\n\n {isLoading && !csvData && (\n <div className=\"text-text-secondary\">Loading...</div>\n )}\n\n <div className={theme.messages.message.csvPreview.tableContainer}>\n {!error && csvData.length > 0 && renderTable(csvData, 6)}\n </div>\n\n <AnimatePresence>\n {isModalOpen && (\n <motion.div\n className={theme.messages.message.csvPreview.dialog.base}\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={{ duration: 0.3 }}\n >\n <motion.div\n ref={modalRef}\n className={theme.messages.message.csvPreview.dialog.container}\n initial={{ scale: 0.8 }}\n animate={{ scale: 1 }}\n exit={{ scale: 0.8 }}\n transition={{ duration: 0.3 }}\n >\n {!error && csvData.length > 0 && renderTable(csvData)}\n </motion.div>\n </motion.div>\n )}\n </AnimatePresence>\n </div>\n );\n};\n\nexport default CSVFileRenderer;\n"],"names":["url","DownloadIcon","PlaceholderIcon"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { jsxs, jsx } from "react/jsx-runtime";
2
2
  import { cn, Ellipsis } from "reablocks";
3
- import { S as SvgFile } from "./index-D20wtImV.js";
3
+ import { S as SvgFile } from "./index-xZVXUqlo.js";
4
4
  const DefaultFileRenderer = ({
5
5
  name,
6
6
  limit = 100,
@@ -12,4 +12,4 @@ const DefaultFileRenderer = ({
12
12
  export {
13
13
  DefaultFileRenderer as default
14
14
  };
15
- //# sourceMappingURL=DefaultFileRenderer-P8Yt2eNK.js.map
15
+ //# sourceMappingURL=DefaultFileRenderer-OnGmLQl4.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"DefaultFileRenderer-P8Yt2eNK.js","sources":["../src/SessionMessages/SessionMessage/MessageFile/renderers/DefaultFileRenderer.tsx"],"sourcesContent":["import { cn, Ellipsis } from 'reablocks';\nimport type { FC, ReactElement } from 'react';\n\nimport FileIcon from '@/assets/file.svg?react';\n\ninterface DefaultFileRendererProps {\n /**\n * Limit for the name.\n */\n limit?: number;\n\n /**\n * Name of the file.\n */\n name?: string;\n\n /**\n * URL of the file.\n */\n url: string;\n\n /**\n * Icon to for file type.\n */\n fileIcon?: ReactElement;\n}\n\n/**\n * Default renderer for unspecified file types.\n */\nconst DefaultFileRenderer: FC<DefaultFileRendererProps> = ({\n name,\n limit = 100,\n fileIcon = <FileIcon />\n}) => (\n <figure className=\"flex items-center gap-2\">\n {fileIcon}\n {name && (\n <figcaption className={cn('file-name-class')}>\n <Ellipsis value={name} limit={limit} />\n </figcaption>\n )}\n </figure>\n);\n\nexport default DefaultFileRenderer;\n"],"names":["FileIcon"],"mappings":";;;AA8BA,MAAM,sBAAoD,CAAC;AAAA,EACzD;AAAA,EACA,QAAQ;AAAA,EACR,+BAAYA,SAAA,CAAA,CAAS;AACvB,MACE,qBAAC,UAAA,EAAO,WAAU,2BACf,UAAA;AAAA,EAAA;AAAA,EACA,QACC,oBAAC,cAAA,EAAW,WAAW,GAAG,iBAAiB,GACzC,UAAA,oBAAC,UAAA,EAAS,OAAO,MAAM,MAAA,CAAc,EAAA,CACvC;AAAA,EAAA,CAEJ;"}
1
+ {"version":3,"file":"DefaultFileRenderer-OnGmLQl4.js","sources":["../src/SessionMessages/SessionMessage/MessageFile/renderers/DefaultFileRenderer.tsx"],"sourcesContent":["import { cn, Ellipsis } from 'reablocks';\nimport type { FC, ReactElement } from 'react';\n\nimport FileIcon from '@/assets/file.svg?react';\n\ninterface DefaultFileRendererProps {\n /**\n * Limit for the name.\n */\n limit?: number;\n\n /**\n * Name of the file.\n */\n name?: string;\n\n /**\n * URL of the file.\n */\n url: string;\n\n /**\n * Icon to for file type.\n */\n fileIcon?: ReactElement;\n}\n\n/**\n * Default renderer for unspecified file types.\n */\nconst DefaultFileRenderer: FC<DefaultFileRendererProps> = ({\n name,\n limit = 100,\n fileIcon = <FileIcon />\n}) => (\n <figure className=\"flex items-center gap-2\">\n {fileIcon}\n {name && (\n <figcaption className={cn('file-name-class')}>\n <Ellipsis value={name} limit={limit} />\n </figcaption>\n )}\n </figure>\n);\n\nexport default DefaultFileRenderer;\n"],"names":["FileIcon"],"mappings":";;;AA8BA,MAAM,sBAAoD,CAAC;AAAA,EACzD;AAAA,EACA,QAAQ;AAAA,EACR,+BAAYA,SAAA,CAAA,CAAS;AACvB,MACE,qBAAC,UAAA,EAAO,WAAU,2BACf,UAAA;AAAA,EAAA;AAAA,EACA,QACC,oBAAC,cAAA,EAAW,WAAW,GAAG,iBAAiB,GACzC,UAAA,oBAAC,UAAA,EAAS,OAAO,MAAM,MAAA,CAAc,EAAA,CACvC;AAAA,EAAA,CAEJ;"}
@@ -4,6 +4,10 @@ export interface CodeHighlighterProps extends PropsWithChildren {
4
4
  * The class name to apply to the code block.
5
5
  */
6
6
  className?: string;
7
+ /**
8
+ * The class name to apply to the inline code.
9
+ */
10
+ inlineClassName?: string;
7
11
  /**
8
12
  * The language of the code block.
9
13
  */
@@ -1,6 +1,10 @@
1
1
  import { FC, PropsWithChildren } from 'react';
2
2
  import { ConversationFile } from '../../types';
3
3
  export interface MessageQuestionProps extends PropsWithChildren {
4
+ /**
5
+ * Number of lines to show before truncating.
6
+ */
7
+ previewLineClamp?: number | boolean;
4
8
  /**
5
9
  * Question to render.
6
10
  */
package/dist/docs.json CHANGED
@@ -105,7 +105,7 @@
105
105
  },
106
106
  "theme": {
107
107
  "defaultValue": {
108
- "value": "{\n base: 'text-content-text-neutral-base',\n console: 'flex w-full gap-4 h-full',\n companion: 'w-full h-full overflow-hidden',\n empty: 'text-center flex-1',\n appbar: 'flex p-5',\n sessions: {\n base: 'overflow-auto',\n console:\n 'min-w-[150px] w-[30%] max-w-[300px] bg-gradient-neutral-200 p-5 rounded-3xl',\n companion: 'w-full h-full',\n group: 'text-xs text-content-text-neutral-3 mt-4 hover:bg-transparent mb-1',\n create: 'relative mb-4 rounded-[10px] text-white',\n session: {\n base: 'group my-1 rounded-[10px] p-2 text-content-text-neutral-1 border border-transparent',\n active:\n 'border-select-menu-items-color-item-stroke-row-hover [&_button]:opacity-100!',\n delete: '[&>svg]:w-4 [&>svg]:h-4 opacity-0 group-hover:opacity-50!'\n }\n },\n messages: {\n base: '',\n console: 'flex flex-col mx-5 flex-1 overflow-hidden',\n companion: 'flex w-full h-full',\n back: 'self-start pl-0 my-2 ',\n inner: 'flex-1 h-full flex flex-col',\n title: 'text-base font-bold',\n date: 'text-xs whitespace-nowrap text-content-text-neutral-2',\n content: 'mt-2 flex-1 overflow-auto',\n header: 'flex justify-between items-center gap-2',\n showMore: 'mb-4',\n message: {\n base: 'mt-4 mb-4 flex flex-col p-0 rounded-sm border-none bg-transparent',\n question:\n 'relative font-semibold mb-4 px-4 py-4 pb-2 rounded-3xl rounded-br-none text-typography border bg-(--background-neutral-raised-5) border-(--stroke-neutral-5)',\n response:\n 'relative data-[compact=false]:px-4 text-content-text-neutral-base',\n overlay:\n \"overflow-y-hidden max-h-[350px] after:content-[''] after:absolute after:inset-x-0 after:bottom-0 after:h-16 after:bg-linear-to-b after:from-transparent after:to-effects-shadows-base-2-xl\",\n cursor: 'inline-block w-1 h-4 bg-current',\n expand: 'absolute bottom-0 right-1 z-10',\n files: {\n base: 'mb-2 flex flex-wrap gap-3 ',\n file: {\n base: 'flex items-center gap-2 border border-stroke-neutral-4 px-3 py-2 rounded-lg cursor-pointer',\n name: 'text-sm text-content-text-neutral-4'\n }\n },\n sources: {\n base: 'my-4 flex flex-wrap gap-3',\n source: {\n base: 'flex gap-2 border border-stroke-neutral-4 px-4 py-2 rounded-lg cursor-pointer',\n companion: 'flex-1 px-3 py-1.5',\n image: 'max-w-10 max-h-10 rounded-md w-full h-fit self-center',\n title: 'text-md block',\n url: 'text-sm text-buttons-colors-link-primary-text-resting underline'\n }\n },\n markdown: {\n hr: 'my-4 border-t border-stroke-neutral-4',\n copy: 'sticky py-1 [&>svg]:w-4 [&>svg]:h-4 opacity-50',\n p: 'mb-2',\n a: 'text-buttons-colors-link-primary-text-resting underline',\n table: 'table-auto w-full m-2',\n th: 'px-4 py-2 text-left font-bold border-b border-stroke-neutral-4',\n td: 'px-4 py-2',\n code: 'm-2 rounded-b relative',\n toolbar:\n 'text-xs flex items-center justify-between px-2 py-1 rounded-t sticky top-0 backdrop-blur-md bg-gradient-neutral-500/50',\n li: 'mb-2 ml-6',\n ul: 'mb-4 list-disc',\n ol: 'mb-4 list-decimal',\n h1: 'text-4xl font-bold mb-4 mt-6',\n h2: 'text-3xl font-bold mb-3 mt-5',\n h3: 'text-2xl font-bold mb-3 mt-4',\n h4: 'text-xl font-bold mb-2 mt-3',\n h5: 'text-lg font-bold mb-2 mt-2',\n h6: 'text-base font-bold mb-2 mt-2'\n },\n csvPreview: {\n base: 'flex flex-col gap-2',\n header: {\n base: 'flex justify-between items-center gap-4',\n icon: 'csv-icon flex items-center',\n actions: 'csv-actions flex items-center gap-6'\n },\n tableContainer: 'flex justify-between',\n dialog: {\n base: 'fixed inset-0 bg-background-neutral-canvas-base/70 flex justify-center items-center z-50',\n container:\n 'bg-background-neutral-canvas-base rounded-md w-11/12 h-5/6 overflow-auto'\n }\n },\n footer: {\n base: 'mt-3 flex gap-1.5',\n copy: 'p-3 rounded-[10px] [&>svg]:size-4 opacity-50 hover:opacity-100!',\n upvote:\n 'p-3 rounded-[10px] [&>svg]:size-4 opacity-50 hover:opacity-100!',\n downvote:\n 'p-3 rounded-[10px] [&>svg]:size-4 opacity-50 hover:opacity-100!',\n refresh:\n 'p-3 rounded-[10px] [&>svg]:size-4 opacity-50 hover:opacity-100!'\n }\n }\n },\n input: {\n base: 'flex mt-4 relative',\n upload:\n 'px-5 py-2 size-10 text-content-text-neutral-2 hover:text-content-text-neutral-base',\n input:\n 'w-full border rounded-3xl px-3 py-2 pr-16 after:hidden after:mx-10! [&>textarea]:w-full [&>textarea]:flex-none [&>textarea]:outline-none [&>textarea]:resize-none',\n actions: {\n base: 'absolute flex gap-2 items-center right-2 inset-y-1/2 -translate-y-1/2 z-10',\n send: 'px-3 py-3 hover:bg-primary-hover rounded-full size-8',\n stop: 'px-2 py-2 bg-content-assets-semantic-error-base text-white rounded-full hover:bg-content-assets-semantic-error-1 size-8'\n }\n }\n}"
108
+ "value": "{\n base: 'text-content-text-neutral-base',\n console: 'flex w-full gap-4 h-full',\n companion: 'w-full h-full overflow-hidden',\n empty: 'text-center flex-1',\n appbar: 'flex p-5',\n sessions: {\n base: 'overflow-auto',\n console:\n 'min-w-[150px] w-[30%] max-w-[300px] bg-gradient-neutral-200 p-5 rounded-3xl',\n companion: 'w-full h-full',\n group: 'text-xs text-content-text-neutral-3 mt-4 hover:bg-transparent mb-1',\n create: 'relative mb-4 rounded-[10px] text-white',\n session: {\n base: 'group my-1 rounded-[10px] p-2 text-content-text-neutral-1 border border-transparent',\n active:\n 'border-select-menu-items-color-item-stroke-row-hover [&_button]:opacity-100!',\n delete: '[&>svg]:w-4 [&>svg]:h-4 opacity-0 group-hover:opacity-50!'\n }\n },\n messages: {\n base: '',\n console: 'flex flex-col mx-5 flex-1 overflow-hidden',\n companion: 'flex w-full h-full',\n back: 'self-start pl-0 my-2 ',\n inner: 'flex-1 h-full flex flex-col',\n title: 'text-base font-bold',\n date: 'text-xs whitespace-nowrap text-content-text-neutral-2',\n content: 'mt-2 flex-1 overflow-auto',\n header: 'flex justify-between items-center gap-2',\n showMore: 'mb-4',\n message: {\n base: 'mt-4 mb-4 flex flex-col p-0 rounded-sm border-none bg-transparent',\n question:\n 'relative font-semibold mb-4 px-4 py-4 pb-2 rounded-3xl rounded-br-none text-typography border bg-(--background-neutral-raised-5) border-(--stroke-neutral-5)',\n response:\n 'relative data-[compact=false]:px-4 text-content-text-neutral-base',\n overlay:\n \"overflow-y-hidden max-h-[350px] after:content-[''] after:absolute after:inset-x-0 after:bottom-0 after:h-16 after:bg-linear-to-b after:from-transparent after:to-effects-shadows-base-2-xl\",\n cursor: 'inline-block w-1 h-4 bg-current',\n expand: 'absolute bottom-0 right-1 z-10',\n files: {\n base: 'mb-2 flex flex-wrap gap-3 ',\n file: {\n base: 'flex items-center gap-2 border border-stroke-neutral-4 px-3 py-2 rounded-lg cursor-pointer',\n name: 'text-sm text-content-text-neutral-4'\n }\n },\n sources: {\n base: 'my-4 flex flex-wrap gap-3',\n source: {\n base: 'flex gap-2 border border-stroke-neutral-4 px-4 py-2 rounded-lg cursor-pointer',\n companion: 'flex-1 px-3 py-1.5',\n image: 'max-w-10 max-h-10 rounded-md w-full h-fit self-center',\n title: 'text-md block',\n url: 'text-sm text-buttons-colors-link-primary-text-resting underline'\n }\n },\n markdown: {\n hr: 'my-4 border-t border-stroke-neutral-4',\n copy: 'sticky py-1 [&>svg]:w-4 [&>svg]:h-4 opacity-50',\n p: 'mb-2',\n a: 'text-buttons-colors-link-primary-text-resting underline',\n table: 'table-auto w-full m-2',\n th: 'px-4 py-2 text-left font-bold border-b border-stroke-neutral-4',\n td: 'px-4 py-2',\n code: 'm-2 rounded-b relative',\n inlineCode: 'bg-gradient-neutral-200 p-1 rounded',\n toolbar:\n 'text-xs flex items-center justify-between px-2 py-1 rounded-t sticky top-0 backdrop-blur-md bg-gradient-neutral-500/50',\n li: 'mb-2 ml-6',\n ul: 'mb-4 list-disc',\n ol: 'mb-4 list-decimal',\n h1: 'text-4xl font-bold mb-4 mt-6',\n h2: 'text-3xl font-bold mb-3 mt-5',\n h3: 'text-2xl font-bold mb-3 mt-4',\n h4: 'text-xl font-bold mb-2 mt-3',\n h5: 'text-lg font-bold mb-2 mt-2',\n h6: 'text-base font-bold mb-2 mt-2'\n },\n csvPreview: {\n base: 'flex flex-col gap-2',\n header: {\n base: 'flex justify-between items-center gap-4',\n icon: 'csv-icon flex items-center',\n actions: 'csv-actions flex items-center gap-6'\n },\n tableContainer: 'flex justify-between',\n dialog: {\n base: 'fixed inset-0 bg-background-neutral-canvas-base/70 flex justify-center items-center z-50',\n container:\n 'bg-background-neutral-canvas-base rounded-md w-11/12 h-5/6 overflow-auto'\n }\n },\n footer: {\n base: 'mt-3 flex gap-1.5',\n copy: 'p-3 rounded-[10px] [&>svg]:size-4 opacity-50 hover:opacity-100!',\n upvote:\n 'p-3 rounded-[10px] [&>svg]:size-4 opacity-50 hover:opacity-100!',\n downvote:\n 'p-3 rounded-[10px] [&>svg]:size-4 opacity-50 hover:opacity-100!',\n refresh:\n 'p-3 rounded-[10px] [&>svg]:size-4 opacity-50 hover:opacity-100!'\n }\n }\n },\n input: {\n base: 'flex mt-4 relative',\n upload:\n 'px-5 py-2 size-10 text-content-text-neutral-2 hover:text-content-text-neutral-base',\n input:\n 'w-full border rounded-3xl px-3 py-2 pr-16 after:hidden after:mx-10! [&>textarea]:w-full [&>textarea]:flex-none [&>textarea]:outline-none [&>textarea]:resize-none',\n actions: {\n base: 'absolute flex gap-2 items-center right-2 inset-y-1/2 -translate-y-1/2 z-10',\n send: 'px-3 py-3 hover:bg-primary-hover rounded-full size-8',\n stop: 'px-2 py-2 bg-content-assets-semantic-error-base text-white rounded-full hover:bg-content-assets-semantic-error-1 size-8'\n }\n }\n}"
109
109
  },
110
110
  "description": "Custom theme for the chat.",
111
111
  "name": "theme",
@@ -327,7 +327,7 @@
327
327
  },
328
328
  "theme": {
329
329
  "defaultValue": {
330
- "value": "{\n base: 'text-content-text-neutral-base',\n console: 'flex w-full gap-4 h-full',\n companion: 'w-full h-full overflow-hidden',\n empty: 'text-center flex-1',\n appbar: 'flex p-5',\n sessions: {\n base: 'overflow-auto',\n console:\n 'min-w-[150px] w-[30%] max-w-[300px] bg-gradient-neutral-200 p-5 rounded-3xl',\n companion: 'w-full h-full',\n group: 'text-xs text-content-text-neutral-3 mt-4 hover:bg-transparent mb-1',\n create: 'relative mb-4 rounded-[10px] text-white',\n session: {\n base: 'group my-1 rounded-[10px] p-2 text-content-text-neutral-1 border border-transparent',\n active:\n 'border-select-menu-items-color-item-stroke-row-hover [&_button]:opacity-100!',\n delete: '[&>svg]:w-4 [&>svg]:h-4 opacity-0 group-hover:opacity-50!'\n }\n },\n messages: {\n base: '',\n console: 'flex flex-col mx-5 flex-1 overflow-hidden',\n companion: 'flex w-full h-full',\n back: 'self-start pl-0 my-2 ',\n inner: 'flex-1 h-full flex flex-col',\n title: 'text-base font-bold',\n date: 'text-xs whitespace-nowrap text-content-text-neutral-2',\n content: 'mt-2 flex-1 overflow-auto',\n header: 'flex justify-between items-center gap-2',\n showMore: 'mb-4',\n message: {\n base: 'mt-4 mb-4 flex flex-col p-0 rounded-sm border-none bg-transparent',\n question:\n 'relative font-semibold mb-4 px-4 py-4 pb-2 rounded-3xl rounded-br-none text-typography border bg-(--background-neutral-raised-5) border-(--stroke-neutral-5)',\n response:\n 'relative data-[compact=false]:px-4 text-content-text-neutral-base',\n overlay:\n \"overflow-y-hidden max-h-[350px] after:content-[''] after:absolute after:inset-x-0 after:bottom-0 after:h-16 after:bg-linear-to-b after:from-transparent after:to-effects-shadows-base-2-xl\",\n cursor: 'inline-block w-1 h-4 bg-current',\n expand: 'absolute bottom-0 right-1 z-10',\n files: {\n base: 'mb-2 flex flex-wrap gap-3 ',\n file: {\n base: 'flex items-center gap-2 border border-stroke-neutral-4 px-3 py-2 rounded-lg cursor-pointer',\n name: 'text-sm text-content-text-neutral-4'\n }\n },\n sources: {\n base: 'my-4 flex flex-wrap gap-3',\n source: {\n base: 'flex gap-2 border border-stroke-neutral-4 px-4 py-2 rounded-lg cursor-pointer',\n companion: 'flex-1 px-3 py-1.5',\n image: 'max-w-10 max-h-10 rounded-md w-full h-fit self-center',\n title: 'text-md block',\n url: 'text-sm text-buttons-colors-link-primary-text-resting underline'\n }\n },\n markdown: {\n hr: 'my-4 border-t border-stroke-neutral-4',\n copy: 'sticky py-1 [&>svg]:w-4 [&>svg]:h-4 opacity-50',\n p: 'mb-2',\n a: 'text-buttons-colors-link-primary-text-resting underline',\n table: 'table-auto w-full m-2',\n th: 'px-4 py-2 text-left font-bold border-b border-stroke-neutral-4',\n td: 'px-4 py-2',\n code: 'm-2 rounded-b relative',\n toolbar:\n 'text-xs flex items-center justify-between px-2 py-1 rounded-t sticky top-0 backdrop-blur-md bg-gradient-neutral-500/50',\n li: 'mb-2 ml-6',\n ul: 'mb-4 list-disc',\n ol: 'mb-4 list-decimal',\n h1: 'text-4xl font-bold mb-4 mt-6',\n h2: 'text-3xl font-bold mb-3 mt-5',\n h3: 'text-2xl font-bold mb-3 mt-4',\n h4: 'text-xl font-bold mb-2 mt-3',\n h5: 'text-lg font-bold mb-2 mt-2',\n h6: 'text-base font-bold mb-2 mt-2'\n },\n csvPreview: {\n base: 'flex flex-col gap-2',\n header: {\n base: 'flex justify-between items-center gap-4',\n icon: 'csv-icon flex items-center',\n actions: 'csv-actions flex items-center gap-6'\n },\n tableContainer: 'flex justify-between',\n dialog: {\n base: 'fixed inset-0 bg-background-neutral-canvas-base/70 flex justify-center items-center z-50',\n container:\n 'bg-background-neutral-canvas-base rounded-md w-11/12 h-5/6 overflow-auto'\n }\n },\n footer: {\n base: 'mt-3 flex gap-1.5',\n copy: 'p-3 rounded-[10px] [&>svg]:size-4 opacity-50 hover:opacity-100!',\n upvote:\n 'p-3 rounded-[10px] [&>svg]:size-4 opacity-50 hover:opacity-100!',\n downvote:\n 'p-3 rounded-[10px] [&>svg]:size-4 opacity-50 hover:opacity-100!',\n refresh:\n 'p-3 rounded-[10px] [&>svg]:size-4 opacity-50 hover:opacity-100!'\n }\n }\n },\n input: {\n base: 'flex mt-4 relative',\n upload:\n 'px-5 py-2 size-10 text-content-text-neutral-2 hover:text-content-text-neutral-base',\n input:\n 'w-full border rounded-3xl px-3 py-2 pr-16 after:hidden after:mx-10! [&>textarea]:w-full [&>textarea]:flex-none [&>textarea]:outline-none [&>textarea]:resize-none',\n actions: {\n base: 'absolute flex gap-2 items-center right-2 inset-y-1/2 -translate-y-1/2 z-10',\n send: 'px-3 py-3 hover:bg-primary-hover rounded-full size-8',\n stop: 'px-2 py-2 bg-content-assets-semantic-error-base text-white rounded-full hover:bg-content-assets-semantic-error-1 size-8'\n }\n }\n}"
330
+ "value": "{\n base: 'text-content-text-neutral-base',\n console: 'flex w-full gap-4 h-full',\n companion: 'w-full h-full overflow-hidden',\n empty: 'text-center flex-1',\n appbar: 'flex p-5',\n sessions: {\n base: 'overflow-auto',\n console:\n 'min-w-[150px] w-[30%] max-w-[300px] bg-gradient-neutral-200 p-5 rounded-3xl',\n companion: 'w-full h-full',\n group: 'text-xs text-content-text-neutral-3 mt-4 hover:bg-transparent mb-1',\n create: 'relative mb-4 rounded-[10px] text-white',\n session: {\n base: 'group my-1 rounded-[10px] p-2 text-content-text-neutral-1 border border-transparent',\n active:\n 'border-select-menu-items-color-item-stroke-row-hover [&_button]:opacity-100!',\n delete: '[&>svg]:w-4 [&>svg]:h-4 opacity-0 group-hover:opacity-50!'\n }\n },\n messages: {\n base: '',\n console: 'flex flex-col mx-5 flex-1 overflow-hidden',\n companion: 'flex w-full h-full',\n back: 'self-start pl-0 my-2 ',\n inner: 'flex-1 h-full flex flex-col',\n title: 'text-base font-bold',\n date: 'text-xs whitespace-nowrap text-content-text-neutral-2',\n content: 'mt-2 flex-1 overflow-auto',\n header: 'flex justify-between items-center gap-2',\n showMore: 'mb-4',\n message: {\n base: 'mt-4 mb-4 flex flex-col p-0 rounded-sm border-none bg-transparent',\n question:\n 'relative font-semibold mb-4 px-4 py-4 pb-2 rounded-3xl rounded-br-none text-typography border bg-(--background-neutral-raised-5) border-(--stroke-neutral-5)',\n response:\n 'relative data-[compact=false]:px-4 text-content-text-neutral-base',\n overlay:\n \"overflow-y-hidden max-h-[350px] after:content-[''] after:absolute after:inset-x-0 after:bottom-0 after:h-16 after:bg-linear-to-b after:from-transparent after:to-effects-shadows-base-2-xl\",\n cursor: 'inline-block w-1 h-4 bg-current',\n expand: 'absolute bottom-0 right-1 z-10',\n files: {\n base: 'mb-2 flex flex-wrap gap-3 ',\n file: {\n base: 'flex items-center gap-2 border border-stroke-neutral-4 px-3 py-2 rounded-lg cursor-pointer',\n name: 'text-sm text-content-text-neutral-4'\n }\n },\n sources: {\n base: 'my-4 flex flex-wrap gap-3',\n source: {\n base: 'flex gap-2 border border-stroke-neutral-4 px-4 py-2 rounded-lg cursor-pointer',\n companion: 'flex-1 px-3 py-1.5',\n image: 'max-w-10 max-h-10 rounded-md w-full h-fit self-center',\n title: 'text-md block',\n url: 'text-sm text-buttons-colors-link-primary-text-resting underline'\n }\n },\n markdown: {\n hr: 'my-4 border-t border-stroke-neutral-4',\n copy: 'sticky py-1 [&>svg]:w-4 [&>svg]:h-4 opacity-50',\n p: 'mb-2',\n a: 'text-buttons-colors-link-primary-text-resting underline',\n table: 'table-auto w-full m-2',\n th: 'px-4 py-2 text-left font-bold border-b border-stroke-neutral-4',\n td: 'px-4 py-2',\n code: 'm-2 rounded-b relative',\n inlineCode: 'bg-gradient-neutral-200 p-1 rounded',\n toolbar:\n 'text-xs flex items-center justify-between px-2 py-1 rounded-t sticky top-0 backdrop-blur-md bg-gradient-neutral-500/50',\n li: 'mb-2 ml-6',\n ul: 'mb-4 list-disc',\n ol: 'mb-4 list-decimal',\n h1: 'text-4xl font-bold mb-4 mt-6',\n h2: 'text-3xl font-bold mb-3 mt-5',\n h3: 'text-2xl font-bold mb-3 mt-4',\n h4: 'text-xl font-bold mb-2 mt-3',\n h5: 'text-lg font-bold mb-2 mt-2',\n h6: 'text-base font-bold mb-2 mt-2'\n },\n csvPreview: {\n base: 'flex flex-col gap-2',\n header: {\n base: 'flex justify-between items-center gap-4',\n icon: 'csv-icon flex items-center',\n actions: 'csv-actions flex items-center gap-6'\n },\n tableContainer: 'flex justify-between',\n dialog: {\n base: 'fixed inset-0 bg-background-neutral-canvas-base/70 flex justify-center items-center z-50',\n container:\n 'bg-background-neutral-canvas-base rounded-md w-11/12 h-5/6 overflow-auto'\n }\n },\n footer: {\n base: 'mt-3 flex gap-1.5',\n copy: 'p-3 rounded-[10px] [&>svg]:size-4 opacity-50 hover:opacity-100!',\n upvote:\n 'p-3 rounded-[10px] [&>svg]:size-4 opacity-50 hover:opacity-100!',\n downvote:\n 'p-3 rounded-[10px] [&>svg]:size-4 opacity-50 hover:opacity-100!',\n refresh:\n 'p-3 rounded-[10px] [&>svg]:size-4 opacity-50 hover:opacity-100!'\n }\n }\n },\n input: {\n base: 'flex mt-4 relative',\n upload:\n 'px-5 py-2 size-10 text-content-text-neutral-2 hover:text-content-text-neutral-base',\n input:\n 'w-full border rounded-3xl px-3 py-2 pr-16 after:hidden after:mx-10! [&>textarea]:w-full [&>textarea]:flex-none [&>textarea]:outline-none [&>textarea]:resize-none',\n actions: {\n base: 'absolute flex gap-2 items-center right-2 inset-y-1/2 -translate-y-1/2 z-10',\n send: 'px-3 py-3 hover:bg-primary-hover rounded-full size-8',\n stop: 'px-2 py-2 bg-content-assets-semantic-error-base text-white rounded-full hover:bg-content-assets-semantic-error-1 size-8'\n }\n }\n}"
331
331
  },
332
332
  "description": "Custom theme for the appbar",
333
333
  "name": "theme",
@@ -812,6 +812,25 @@
812
812
  "name": "string"
813
813
  }
814
814
  },
815
+ "inlineClassName": {
816
+ "defaultValue": null,
817
+ "description": "The class name to apply to the inline code.",
818
+ "name": "inlineClassName",
819
+ "parent": {
820
+ "fileName": "src/Markdown/CodeHighlighter.tsx",
821
+ "name": "CodeHighlighterProps"
822
+ },
823
+ "declarations": [
824
+ {
825
+ "fileName": "src/Markdown/CodeHighlighter.tsx",
826
+ "name": "CodeHighlighterProps"
827
+ }
828
+ ],
829
+ "required": false,
830
+ "type": {
831
+ "name": "string"
832
+ }
833
+ },
815
834
  "language": {
816
835
  "defaultValue": null,
817
836
  "description": "The language of the code block.",
@@ -1659,6 +1678,27 @@
1659
1678
  "displayName": "MessageQuestion",
1660
1679
  "methods": [],
1661
1680
  "props": {
1681
+ "previewLineClamp": {
1682
+ "defaultValue": {
1683
+ "value": "3"
1684
+ },
1685
+ "description": "Number of lines to show before truncating.",
1686
+ "name": "previewLineClamp",
1687
+ "parent": {
1688
+ "fileName": "src/SessionMessages/SessionMessage/MessageQuestion.tsx",
1689
+ "name": "MessageQuestionProps"
1690
+ },
1691
+ "declarations": [
1692
+ {
1693
+ "fileName": "src/SessionMessages/SessionMessage/MessageQuestion.tsx",
1694
+ "name": "MessageQuestionProps"
1695
+ }
1696
+ ],
1697
+ "required": false,
1698
+ "type": {
1699
+ "name": "number | boolean"
1700
+ }
1701
+ },
1662
1702
  "question": {
1663
1703
  "defaultValue": null,
1664
1704
  "description": "Question to render.",
@@ -76,6 +76,7 @@ const chatTheme = {
76
76
  th: "px-4 py-2 text-left font-bold border-b border-stroke-neutral-4",
77
77
  td: "px-4 py-2",
78
78
  code: "m-2 rounded-b relative",
79
+ inlineCode: "bg-gradient-neutral-200 p-1 rounded",
79
80
  toolbar: "text-xs flex items-center justify-between px-2 py-1 rounded-t sticky top-0 backdrop-blur-md bg-gradient-neutral-500/50",
80
81
  li: "mb-2 ml-6",
81
82
  ul: "mb-4 list-disc",
@@ -1465,14 +1466,17 @@ const light = {
1465
1466
  const CodeHighlighter = ({
1466
1467
  className,
1467
1468
  children,
1469
+ inlineClassName,
1468
1470
  copyClassName,
1469
1471
  copyIcon = /* @__PURE__ */ jsx(SvgCopy, {}),
1470
1472
  language,
1471
1473
  toolbarClassName,
1472
- theme = dark
1474
+ theme = dark,
1475
+ ...props
1473
1476
  }) => {
1474
1477
  const match = language?.match(/language-(\w+)/);
1475
1478
  const lang = match ? match[1] : "text";
1479
+ const isInline = !match;
1476
1480
  const handleCopy = (text) => {
1477
1481
  navigator.clipboard.writeText(text).then(() => {
1478
1482
  console.log("Text copied to clipboard");
@@ -1480,6 +1484,9 @@ const CodeHighlighter = ({
1480
1484
  console.error("Could not copy text: ", err);
1481
1485
  });
1482
1486
  };
1487
+ if (isInline) {
1488
+ return /* @__PURE__ */ jsx("code", { className: cn(inlineClassName, className), ...props, children });
1489
+ }
1483
1490
  return /* @__PURE__ */ jsxs("div", { className: cn("relative", className), children: [
1484
1491
  /* @__PURE__ */ jsxs("div", { className: cn(toolbarClassName), children: [
1485
1492
  /* @__PURE__ */ jsx("div", { children: lang }),
@@ -1516,7 +1523,8 @@ const Markdown = ({
1516
1523
  CodeHighlighter,
1517
1524
  {
1518
1525
  ...props,
1519
- language: className,
1526
+ language: cn(className),
1527
+ inlineClassName: cn(theme.messages.message.markdown.inlineCode),
1520
1528
  className: cn(theme.messages.message.markdown.code, className),
1521
1529
  copyClassName: cn(theme.messages.message.markdown.copy),
1522
1530
  toolbarClassName: cn(theme.messages.message.markdown.toolbar)
@@ -1656,9 +1664,9 @@ ${response}`),
1656
1664
  };
1657
1665
  const SvgFile = (props) => /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: 16, height: 16, viewBox: "0 0 16 16", fill: "currentColor", ...props }, /* @__PURE__ */ React.createElement("path", { fillRule: "evenodd", clipRule: "evenodd", d: "M2.7036 1.37034C3.04741 1.02653 3.51373 0.833374 3.99996 0.833374H9.33329H9.33331C9.47275 0.833374 9.59885 0.890449 9.68954 0.98251L13.6843 4.97722C13.7763 5.0679 13.8333 5.19398 13.8333 5.33337L13.8333 5.3379V13.3334C13.8333 13.8196 13.6401 14.2859 13.2963 14.6297C12.9525 14.9736 12.4862 15.1667 12 15.1667H3.99996C3.51373 15.1667 3.04741 14.9736 2.7036 14.6297C2.35978 14.2859 2.16663 13.8196 2.16663 13.3334V2.66671C2.16663 2.18048 2.35978 1.71416 2.7036 1.37034ZM3.99996 1.83337H8.83331V5.33337C8.83331 5.60952 9.05717 5.83337 9.33331 5.83337H12.8333V13.3334C12.8333 13.5544 12.7455 13.7663 12.5892 13.9226C12.4329 14.0789 12.221 14.1667 12 14.1667H3.99996C3.77895 14.1667 3.56698 14.0789 3.4107 13.9226C3.25442 13.7663 3.16663 13.5544 3.16663 13.3334V2.66671C3.16663 2.44569 3.25442 2.23373 3.4107 2.07745C3.56698 1.92117 3.77895 1.83337 3.99996 1.83337ZM9.83331 2.5405L12.1262 4.83337H9.83331V2.5405ZM5.33331 8.16663C5.05717 8.16663 4.83331 8.39048 4.83331 8.66663C4.83331 8.94277 5.05717 9.16663 5.33331 9.16663H10.6666C10.9428 9.16663 11.1666 8.94277 11.1666 8.66663C11.1666 8.39048 10.9428 8.16663 10.6666 8.16663H5.33331ZM4.83331 11.3334C4.83331 11.0572 5.05717 10.8334 5.33331 10.8334H10.6666C10.9428 10.8334 11.1666 11.0572 11.1666 11.3334C11.1666 11.6095 10.9428 11.8334 10.6666 11.8334H5.33331C5.05717 11.8334 4.83331 11.6095 4.83331 11.3334ZM5.33331 5.5C5.05717 5.5 4.83331 5.72386 4.83331 6C4.83331 6.27614 5.05717 6.5 5.33331 6.5H6.66665C6.94279 6.5 7.16665 6.27614 7.16665 6C7.16665 5.72386 6.94279 5.5 6.66665 5.5H5.33331Z" }));
1658
1666
  const DefaultFileRenderer = lazy(
1659
- () => import("./DefaultFileRenderer-P8Yt2eNK.js")
1667
+ () => import("./DefaultFileRenderer-OnGmLQl4.js")
1660
1668
  );
1661
- const CSVFileRenderer = lazy(() => import("./CSVFileRenderer-C68XRlb0.js"));
1669
+ const CSVFileRenderer = lazy(() => import("./CSVFileRenderer-DTl0pUQa.js"));
1662
1670
  const ImageFileRenderer = lazy(() => import("./ImageFileRenderer-C8tVW3I8.js"));
1663
1671
  const PDFFileRenderer = lazy(() => import("./PDFFileRenderer-BBn2EVrV.js"));
1664
1672
  const FILE_TYPE_RENDERER_MAP = {
@@ -1749,30 +1757,60 @@ const MessageFiles = ({ files, children }) => {
1749
1757
  };
1750
1758
  const MessageQuestion = ({
1751
1759
  children,
1760
+ previewLineClamp = 3,
1752
1761
  ...props
1753
1762
  }) => {
1754
1763
  const { theme, remarkPlugins } = useContext(ChatContext);
1755
1764
  const { question, files } = props;
1756
1765
  const Comp = children ? Slot : "div";
1757
- const [expanded, setExpanded] = useState(false);
1758
- const isLong = question.length > 500;
1766
+ const [truncated, setTruncated] = useState(false);
1767
+ const [showAll, setShowAll] = useState(false);
1768
+ const ref = useRef(null);
1769
+ const calculateTruncation = useCallback(() => {
1770
+ const el = ref.current;
1771
+ setTruncated(el.scrollHeight > el.clientHeight);
1772
+ }, []);
1773
+ useEffect(() => {
1774
+ const el = ref.current;
1775
+ if (!el || previewLineClamp === false) return;
1776
+ calculateTruncation();
1777
+ const resizeObserver = new ResizeObserver(() => {
1778
+ calculateTruncation();
1779
+ });
1780
+ resizeObserver.observe(el);
1781
+ window.addEventListener("resize", calculateTruncation);
1782
+ return () => {
1783
+ resizeObserver.disconnect();
1784
+ window.removeEventListener("resize", calculateTruncation);
1785
+ };
1786
+ }, [calculateTruncation, previewLineClamp]);
1759
1787
  return /* @__PURE__ */ jsx(
1760
1788
  Comp,
1761
1789
  {
1762
1790
  className: cn(theme.messages.message.question, {
1763
- [theme.messages.message.overlay]: isLong && !expanded
1791
+ [theme.messages.message.overlay]: truncated
1764
1792
  }),
1765
1793
  ...props,
1766
1794
  children: children || /* @__PURE__ */ jsxs(Fragment, { children: [
1767
1795
  /* @__PURE__ */ jsx(MessageFiles, { files }),
1768
- /* @__PURE__ */ jsx(Markdown, { remarkPlugins, theme, children: question }),
1769
- isLong && !expanded && /* @__PURE__ */ jsx(
1796
+ /* @__PURE__ */ jsx(
1797
+ "div",
1798
+ {
1799
+ ref,
1800
+ className: cn({
1801
+ [`line-clamp-${previewLineClamp}`]: !showAll && typeof previewLineClamp === "number",
1802
+ "line-clamp-none": showAll || previewLineClamp === false
1803
+ }),
1804
+ children: /* @__PURE__ */ jsx(Markdown, { remarkPlugins, theme, children: question })
1805
+ }
1806
+ ),
1807
+ truncated && !showAll && /* @__PURE__ */ jsx(
1770
1808
  Button,
1771
1809
  {
1772
1810
  variant: "link",
1773
1811
  size: "small",
1774
1812
  className: theme.messages.message.expand,
1775
- onClick: () => setExpanded(true),
1813
+ onClick: () => setShowAll(true),
1776
1814
  children: "Show more"
1777
1815
  }
1778
1816
  )
@@ -2286,4 +2324,4 @@ export {
2286
2324
  SessionsGroup as y,
2287
2325
  SessionsList as z
2288
2326
  };
2289
- //# sourceMappingURL=index-D20wtImV.js.map
2327
+ //# sourceMappingURL=index-xZVXUqlo.js.map