diff --git a/frontend/components/Markdown.tsx b/frontend/components/Markdown.tsx index 9ee8e3d2..46f02ee1 100644 --- a/frontend/components/Markdown.tsx +++ b/frontend/components/Markdown.tsx @@ -6,7 +6,16 @@ import rehypeHighlight from 'rehype-highlight'; import rehypeKatex from 'rehype-katex'; import 'katex/dist/katex.min.css'; import { useTheme } from '../contexts/ThemeContext'; -import { ClipboardCheck, Clipboard, ChevronDown, ChevronUp, WrapText, Brain } from 'lucide-react'; +import { + ClipboardCheck, + Clipboard, + ChevronDown, + ChevronUp, + WrapText, + Brain, + Eye, +} from 'lucide-react'; +import { Modal } from './ui/Modal'; interface MarkdownProps { text: string; @@ -327,6 +336,19 @@ const MarkdownComponents: any = { const [copied, setCopied] = React.useState(false); const [isCollapsed, setIsCollapsed] = React.useState(false); const [isWrapped, setIsWrapped] = React.useState(false); + const [showHtmlPreview, setShowHtmlPreview] = React.useState(false); + + // Helper function to check if code is a full HTML page + const isFullHtmlPage = (code: string): boolean => { + if (!code) return false; + const trimmed = code.trim().toLowerCase(); + // Check for DOCTYPE, html tag, or both head and body tags + return ( + trimmed.includes('') || + (trimmed.includes('')) || + (trimmed.includes(' { try { @@ -362,6 +384,10 @@ const MarkdownComponents: any = { const codeClassName = childEl?.props?.className || ''; const languageMatch = codeClassName.match(/language-(\w+)/); const language = languageMatch ? languageMatch[1] : null; + + // Get code content for HTML detection + const codeContent = typeof childEl?.props?.children === 'string' ? childEl.props.children : ''; + const isHtml = language === 'html' && isFullHtmlPage(codeContent); const wrappedChild = React.isValidElement(childEl) ? React.cloneElement(childEl as React.ReactElement, { className: `${codeClassName} ${ @@ -371,80 +397,112 @@ const MarkdownComponents: any = { : childEl; return ( -
-        {/* Header with language and copy button */}
-        
- {language && {language}} -
- - - + + {isHtml && ( + )} - - {isCollapsed ? 'Expand code block' : 'Collapse code block'} - - + +
- - {/* padded, scrollable code area */} -
- {wrappedChild} -
-
+ {/* padded, scrollable code area */} +
+ {wrappedChild} +
+ + + {/* HTML Preview Modal */} + {isHtml && ( + setShowHtmlPreview(false)} + title="HTML Preview" + maxWidthClassName="max-w-6xl" + > +