import { useEffect, useRef, useState, useCallback } from 'react' import { motion, AnimatePresence } from 'framer-motion' import { Copy, Download, Sparkles, Loader2, CheckCircle2, ChevronDown, Database } from 'lucide-react' import ReactMarkdown from 'react-markdown' import DOMPurify from 'dompurify' export default function ResultPanel({ result, loading, imagePreview, onCopy, onDownload, onCommitJob, commitLoading, commitResult }) { const canvasRef = useRef(null) const imgRef = useRef(null) const [showAdvanced, setShowAdvanced] = useState(false) const [imageLoaded, setImageLoaded] = useState(false) // Check if text looks like HTML (model outputs HTML, not markdown) const isHTML = result?.text && ( result.text.includes('') || result.text.includes('') || result.text.includes('') || result.text.includes(' { if (!result?.boxes?.length || !canvasRef.current || !imgRef.current) { console.log('❌ Cannot draw - missing:', { hasBoxes: !!result?.boxes?.length, hasCanvas: !!canvasRef.current, hasImgRef: !!imgRef.current }) return } console.log('🎨 Drawing boxes:', result.boxes) const img = imgRef.current const canvas = canvasRef.current const ctx = canvas.getContext('2d') console.log('📐 Image dimensions:', { displayWidth: img.offsetWidth, displayHeight: img.offsetHeight, naturalWidth: img.naturalWidth, naturalHeight: img.naturalHeight, imageDims: result.image_dims }) // Set canvas size to match displayed image canvas.width = img.offsetWidth canvas.height = img.offsetHeight ctx.clearRect(0, 0, canvas.width, canvas.height) // Calculate scale factors const scaleX = img.offsetWidth / (result.image_dims?.w || img.naturalWidth) const scaleY = img.offsetHeight / (result.image_dims?.h || img.naturalHeight) console.log('📏 Scale factors:', { scaleX, scaleY }) // Draw boxes result.boxes.forEach((box, idx) => { const [x1, y1, x2, y2] = box.box const colors = [ '#00ff00', '#00ffff', '#ff00ff', '#ffff00', '#ff0066' ] const color = colors[idx % colors.length] // Scale coordinates const sx = x1 * scaleX const sy = y1 * scaleY const sw = (x2 - x1) * scaleX const sh = (y2 - y1) * scaleY console.log(`📦 Box ${idx} (${box.label}):`, { original: [x1, y1, x2, y2], scaled: [sx, sy, sx + sw, sy + sh], dimensions: { width: sw, height: sh } }) // Draw semi-transparent fill ctx.fillStyle = color + '33' ctx.fillRect(sx, sy, sw, sh) // Draw thick neon border ctx.strokeStyle = color ctx.lineWidth = 4 ctx.shadowColor = color ctx.shadowBlur = 10 ctx.strokeRect(sx, sy, sw, sh) ctx.shadowBlur = 0 // Label background if (box.label) { ctx.font = 'bold 14px Inter' const metrics = ctx.measureText(box.label) const padding = 8 const labelHeight = 24 ctx.fillStyle = color ctx.fillRect(sx, sy - labelHeight, metrics.width + padding * 2, labelHeight) // Label text ctx.fillStyle = '#000' ctx.fillText(box.label, sx + padding, sy - 7) } }) console.log('✅ Finished drawing', result.boxes.length, 'boxes') }, [result]) // Trigger drawing when image loads useEffect(() => { if (imageLoaded && result?.boxes?.length) { console.log('🚀 Image loaded, drawing boxes now') drawBoxes() } }, [imageLoaded, result, drawBoxes]) // Reset imageLoaded when result changes useEffect(() => { setImageLoaded(false) }, [result]) // Redraw on window resize useEffect(() => { if (!imageLoaded || !result?.boxes?.length) return const handleResize = () => { console.log('📐 Window resized, redrawing') drawBoxes() } window.addEventListener('resize', handleResize) return () => window.removeEventListener('resize', handleResize) }, [imageLoaded, result, drawBoxes]) return (

Results

{result && (
)}
{loading ? (

Processing your image with AI magic...

) : result ? ( {/* Preview with boxes */} {imagePreview && result.boxes && result.boxes.length > 0 && (
Result { console.log('🖼️ Image loaded, triggering draw') setImageLoaded(true) }} />
)} {/* Text result */}
{isHTML ? (
) : isMarkdown ? (
{result.text}
) : (
                  {result.text}
                
)}
{/* Raw Response Viewer */} {result.raw_text && (
🔍 Raw Model Response

Unprocessed output from the model (useful for debugging)

                      {result.raw_text}
                    
{result.raw_text.length} characters
)} {/* Advanced Settings Dropdown */}
⚙️ Metadata & Debug Info
{result.metadata && (

Processing Metadata

                      {JSON.stringify(result.metadata, null, 2)}
                    
)} {result.boxes?.length > 0 && (

Parsed Bounding Boxes ({result.boxes.length})

{result.boxes.map((box, idx) => (
Box {idx + 1}:{' '} {box.label}{' '} [{box.box.map(n => Math.round(n)).join(', ')}]
))}

Coordinates are scaled from model output (0-999) to image pixels

)}
{/* Commit Job button (plain_ocr only) */} {onCommitJob && (
{commitLoading ? ( <> Committing Job... ) : commitResult?.success ? ( <> Job Committed ) : ( <> Commit Job )} {commitResult?.success && (

Job saved — ID: {commitResult.job?.id}

)} {commitResult && !commitResult.success && (

{commitResult.error}

)}
)} {/* Success indicator */} Processing complete! ) : (

Ready to process

Upload an image and hit analyze to see the magic!

)}
) }