import { useEffect, useState, useMemo } from 'react'; import MarkdownRenderer from './MarkdownRenderer'; import { X, Copy, Check, Download, PanelRightClose, PanelRightOpen } from 'lucide-react'; import 'highlight.js/styles/github-dark.css'; interface FilePreviewModalProps { isOpen: boolean; onClose: () => void; content: string; } // Helper to generate IDs from text const slugify = (text: string) => { return text .toString() .toLowerCase() .trim() .replace(/\s+/g, '-') .replace(/[^\w\u4e00-\u9fa5-]+/g, '') .replace(/-+/g, '-'); }; export default function FilePreviewModal({ isOpen, onClose, content }: FilePreviewModalProps) { const [copied, setCopied] = useState(false); const [showToc, setShowToc] = useState(false); useEffect(() => { if (isOpen) { document.body.style.overflow = 'hidden'; } else { document.body.style.overflow = 'unset'; } return () => { document.body.style.overflow = 'unset'; }; }, [isOpen]); // Generate TOC data const toc = useMemo(() => { const lines = content.split('\n'); const headers: { level: number; text: string; id: string }[] = []; // Simple regex to match headers. Note: this won't handle code blocks correctly if they contain # // But for a simple TOC it's usually "good enough". // A more robust way would be to traverse the AST, but that requires more setup. let inCodeBlock = false; lines.forEach(line => { if (line.trim().startsWith('```')) { inCodeBlock = !inCodeBlock; return; } if (inCodeBlock) return; const match = line.match(/^(#{1,3})\s+(.+)$/); if (match) { const level = match[1].length; const text = match[2].trim(); const id = slugify(text); headers.push({ level, text, id }); } }); return headers; }, [content]); if (!isOpen) return null; const handleCopy = () => { navigator.clipboard.writeText(content); setCopied(true); setTimeout(() => setCopied(false), 2000); }; const handleDownload = () => { const blob = new Blob([content], { type: 'text/markdown' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = 'content.md'; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); }; const scrollToHeader = (id: string) => { const element = document.getElementById(id); if (element) { element.scrollIntoView({ behavior: 'smooth' }); } }; // Custom components for ReactMarkdown to add IDs - Removed as we switched to marked // In a full implementation we would configure marked slugger or use a custom renderer for headers // For now, TOC scrolling relies on ids that might not be present if we don't add them. // marked adds ids to headers by default (gfm: true). // We can verify this or use a simple post-processing/custom renderer if needed. // For this "change parser" request, let's stick to default marked behavior first. return (
e.stopPropagation()} > {/* Header */}

Markdown Preview

Read-only mode

{/* TOC Toggle */} {toc.length > 0 && ( )}
{/* Body */}
{/* Content */}
{/* TOC Sidebar */} {toc.length > 0 && showToc && (

Table of Contents

)}
); }