'use client' import { useState, useMemo, useRef, useCallback } from 'react' import { motion, AnimatePresence } from 'framer-motion' import { Download, Copy, Check, ExternalLink, RefreshCw, FileText, Archive, Eye, Files, Lightbulb, Clock, Hash, ChevronDown, List, ChevronRight } from 'lucide-react' import type { ExtractionResult, Document, ExportFormat } from '@/types' import { downloadFile, downloadZip, copyToClipboard, getHostname, calculateTotalSize } from '@/lib/download' import { exportToFormat, getFilenameForFormat, getExtensionForFormat, EXPORT_FORMATS } from '@/lib/exporters' import CodeBlock from './CodeBlock' interface OutputSectionProps { result: ExtractionResult onReset: () => void } type TabType = 'documents' | 'preview' | 'download' interface TOCItem { id: string title: string level: number } // Extract TOC from markdown content function extractTOC(content: string): TOCItem[] { const lines = content.split('\n') const toc: TOCItem[] = [] lines.forEach((line, index) => { const match = line.match(/^(#{1,3})\s+(.+)$/) if (match) { const level = match[1].length const title = match[2].trim() const id = `heading-${index}-${title.toLowerCase().replace(/[^a-z0-9]+/g, '-')}` toc.push({ id, title, level }) } }) return toc } export default function OutputSection({ result, onReset }: OutputSectionProps) { const [activeTab, setActiveTab] = useState('documents') const [copied, setCopied] = useState(null) const [selectedDocument, setSelectedDocument] = useState(result.fullDocument) const [isDownloadingZip, setIsDownloadingZip] = useState(false) const [showTOC, setShowTOC] = useState(false) const [showLineNumbers, setShowLineNumbers] = useState(false) const [exportFormat, setExportFormat] = useState('markdown') const previewRef = useRef(null) // Memoize TOC extraction const toc = useMemo(() => extractTOC(selectedDocument.content), [selectedDocument.content]) const handleCopy = useCallback(async (text: string, id: string) => { const success = await copyToClipboard(text) if (success) { setCopied(id) setTimeout(() => setCopied(null), 2000) } }, []) const handleDownloadFile = useCallback((doc: Document) => { downloadFile(doc.content, doc.filename) }, []) // Download in selected format const handleDownloadFormatted = useCallback(() => { const content = exportToFormat(result, exportFormat) const siteName = getHostname(result.url).replace('www.', '').split('.')[0] const filename = getFilenameForFormat(siteName, exportFormat) const blob = new Blob([content], { type: 'text/plain' }) const url = URL.createObjectURL(blob) const a = document.createElement('a') a.href = url a.download = filename document.body.appendChild(a) a.click() document.body.removeChild(a) URL.revokeObjectURL(url) }, [result, exportFormat]) const handleDownloadZip = useCallback(async () => { setIsDownloadingZip(true) try { await downloadZip( result.documents, result.fullDocument, result.agentGuide, getHostname(result.url) ) } catch (error) { console.error('Failed to download ZIP:', error) } finally { setIsDownloadingZip(false) } }, [result]) // Scroll to section handler const scrollToSection = useCallback((id: string) => { const element = document.getElementById(id) if (element && previewRef.current) { element.scrollIntoView({ behavior: 'smooth', block: 'start' }) } }, []) const tabs = [ { id: 'documents' as const, label: 'Documents', icon: Files }, { id: 'preview' as const, label: 'Preview', icon: Eye }, { id: 'download' as const, label: 'Download', icon: Archive }, ] // All documents including full and agent guide const allDocuments = useMemo(() => [result.fullDocument, result.agentGuide, ...result.documents], [result] ) const totalFileCount = allDocuments.length // Keyboard navigation for tabs const handleTabKeyDown = useCallback((e: React.KeyboardEvent, tabId: TabType) => { const tabIds: TabType[] = ['documents', 'preview', 'download'] const currentIndex = tabIds.indexOf(tabId) if (e.key === 'ArrowRight') { e.preventDefault() const nextIndex = (currentIndex + 1) % tabIds.length setActiveTab(tabIds[nextIndex]) } else if (e.key === 'ArrowLeft') { e.preventDefault() const prevIndex = (currentIndex - 1 + tabIds.length) % tabIds.length setActiveTab(tabIds[prevIndex]) } }, []) return ( {/* Success header */}

Extraction Complete

Found {result.documents.length} sections from{' '} {getHostname(result.url)}

{/* Stats grid - responsive */}
Documents
{totalFileCount}
Tokens
{result.stats.totalTokens.toLocaleString()}
Time
{result.stats.processingTime}ms
{/* Linked Sources Info - show when docs were fetched from multiple llms.txt files */} {result.linkedSources && result.linkedSources.length > 0 && (
Content from {result.linkedSources.length + 1} documentation sources
This site uses multiple llms.txt files. All content has been merged for you.
Main {result.linkedSources.map((source, i) => ( {source.name} ))}
)}
{/* Tabs */}
{/* Tab buttons - stack on mobile */}
{tabs.map((tab) => ( ))}
{/* Documents Tab */} {activeTab === 'documents' && ( {/* Featured documents */}
{/* Document list */} {result.documents.length > 0 && ( <>

Individual Sections

{result.documents.map((doc, index) => (
{doc.filename}
{doc.title}
{doc.tokens.toLocaleString()} tokens
))}
)}
)} {/* Preview Tab */} {activeTab === 'preview' && ( {/* Controls bar */}
{/* Document selector */}
{/* Action buttons */}
{/* TOC toggle */} {toc.length > 0 && ( )} {/* Line numbers toggle */} {/* Copy button */}
{/* Content area with optional TOC */}
{/* TOC sidebar */} {showTOC && toc.length > 0 && (

Contents

)}
{/* Preview content */}
)} {/* Download Tab */} {activeTab === 'download' && (

Download Everything

Get all {totalFileCount} documents in a single ZIP file, including the complete documentation and AI agent guide.

{/* Format Selector */}
{totalFileCount} files {calculateTotalSize(result.documents, result.fullDocument, result.agentGuide)}
{/* Quick download buttons */}
)}
{/* Pro tip */}

Pro tip

You can usually access this content directly by adding{' '} /llms-full.txt {' '} to the end of most documentation URLs.

) }