import React, { useState } from 'react'; import { useTranslation } from 'react-i18next'; import { Button } from '../../ui/button'; import { ChevronDown, ChevronUp } from 'lucide-react'; /** * Props for the FormattedDocument component. */ interface FormattedDocumentProps { /** The raw markdown string to be converted and displayed */ content: string; /** Maximum number of characters to show in the collapsed preview state */ maxPreviewLength?: number; /** Additional CSS classes for the container */ className?: string; } /** * Simple Markdown-to-HTML formatter with collapsible preview. * * @description * Converts basic markdown syntax (headers, bold, lists, checkboxes) into styled * Tailwind HTML elements. Includes a "See more/less" toggle for long documents. * * @ai-rules * 1. Use this component to display AI-generated text or documentation strings. * 2. It supports standard markdown symbols: #, ##, ###, **, *, -, [ ], [x]. * 3. `maxPreviewLength` defaults to 500 characters. */ export function FormattedDocument({ content, maxPreviewLength = 500, className = '', }: FormattedDocumentProps) { const [isExpanded, setIsExpanded] = useState(false); const { t } = useTranslation(); /** * Converts simple Markdown strings to HTML using regex patterns. */ const convertMarkdownToHtml = (markdown: string): string => { let html = markdown; // Headers (with improved styling, reduced spacing) html = html.replace( /^### (.*$)/gim, '

$1

' ); html = html.replace( /^## (.*$)/gim, '

$1

' ); html = html.replace( /^# (.*$)/gim, '

$1

' ); // Bold html = html.replace( /\*\*(.*?)\*\*/g, '$1' ); // Italic html = html.replace(/\*(.*?)\*/g, '$1'); // Checkboxes (Process before lists to avoid collisions) html = html.replace( /^[-*+] \[ \] (.*$)/gim, '
$1
' ); html = html.replace( /^[-*+] \[x\] (.*$)/gim, '
$1
' ); // Lists (Bullet and Numbered) - Wrap blocks of items in ul/ol html = html.replace(/((?:^[-*+] .*$(?:\n|$))+)/gim, match => { const items = match .trim() .split('\n') .map(item => `
  • ${item.replace(/^[-*+] /i, '')}
  • `) .join(''); return ``; }); html = html.replace(/((?:^\d+\. .*$(?:\n|$))+)/gim, match => { const items = match .trim() .split('\n') .map(item => `
  • ${item.replace(/^\d+\. /i, '')}
  • `) .join(''); return `
      ${items}
    `; }); // Horizontal rule html = html.replace(/^---$/gim, '
    '); // Line breaks - treat single \n as space, \n\n as paragraph break html = html.replace(/\n\n+/g, '

    '); html = html.replace(/\n/g, ' '); // Wrap in paragraph html = '

    ' + html + '

    '; return html; }; const htmlContent = convertMarkdownToHtml(content); const isLong = content.length > maxPreviewLength; // If not long or if expanded, show everything const displayContent = !isLong || isExpanded ? htmlContent : convertMarkdownToHtml(content.substring(0, maxPreviewLength) + '...'); return (
    {isLong && ( )}
    ); }