diff --git a/App.tsx b/App.tsx index 8290833..a1cef50 100644 --- a/App.tsx +++ b/App.tsx @@ -44,6 +44,14 @@ const App: React.FC = () => { }; const reset = () => { + // Cleanup blob URLs to prevent memory leaks + if (formFile?.previewUrl && formFile.previewUrl.startsWith('blob:')) { + URL.revokeObjectURL(formFile.previewUrl); + } + if (sourceFile?.previewUrl && sourceFile.previewUrl.startsWith('blob:')) { + URL.revokeObjectURL(sourceFile.previewUrl); + } + setStatus(AppStatus.IDLE); setFormFile(null); setSourceFile(null); @@ -223,4 +231,4 @@ const App: React.FC = () => { ); }; -export default App; +export default App; \ No newline at end of file diff --git a/components/FileUpload.tsx b/components/FileUpload.tsx index b77d2de..1ba5c06 100644 --- a/components/FileUpload.tsx +++ b/components/FileUpload.tsx @@ -21,6 +21,9 @@ export const FileUpload: React.FC = ({ const [isDragging, setIsDragging] = useState(false); const processFile = (file: File) => { + // Create a robust Blob URL for previewing (works better than Base64 for PDFs) + const objectUrl = URL.createObjectURL(file); + const reader = new FileReader(); reader.onload = () => { const base64String = reader.result as string; @@ -29,7 +32,7 @@ export const FileUpload: React.FC = ({ onFileSelect({ file, - previewUrl: file.type.startsWith('image/') ? base64String : null, + previewUrl: objectUrl, base64: base64Content, type: file.type as any }); @@ -92,10 +95,10 @@ export const FileUpload: React.FC = ({ ) : (
- {selectedFile.previewUrl ? ( - Preview - ) : ( + {selectedFile.type === 'application/pdf' ? ( + ) : ( + Preview )}
diff --git a/components/ReviewPanel.tsx b/components/ReviewPanel.tsx index 4636b1a..2e6ca57 100644 --- a/components/ReviewPanel.tsx +++ b/components/ReviewPanel.tsx @@ -1,6 +1,6 @@ import React, { useState, useEffect, useMemo, useRef } from 'react'; import { ExtractedField, FileData } from '../types'; -import { Check, Edit2, Download, RefreshCw, FileText, AlertTriangle, XCircle, ArrowRight, PenTool, CheckCircle2, Circle, LayoutTemplate, List, Move } from 'lucide-react'; +import { Check, Edit2, Download, RefreshCw, FileText, AlertTriangle, XCircle, ArrowRight, PenTool, CheckCircle2, Circle, LayoutTemplate, List, Move, ExternalLink } from 'lucide-react'; import { createFilledPdf } from '../services/pdfService'; import { jsPDF } from "jspdf"; @@ -43,9 +43,10 @@ export const ReviewPanel: React.FC = ({ // Generate preview for PDF download useEffect(() => { let active = true; - let timeoutId: ReturnType; + let timeoutId: any; const generatePreview = async () => { + // If it's a PDF, we try to show the filled version if (formFile.type === 'application/pdf') { try { const filledPdfBytes = await createFilledPdf(formFile.base64, fields, isFillablePdf); @@ -60,14 +61,17 @@ export const ReviewPanel: React.FC = ({ }); } catch (e) { console.error("Failed to generate PDF preview", e); + // Fallback to original if generation fails + setPreviewUrl(formFile.previewUrl); } } else { + // For images, just show the original setPreviewUrl(formFile.previewUrl); } }; - // Debounce to avoid excessive PDF generation - timeoutId = setTimeout(generatePreview, 600); + // Debounce to avoid excessive PDF generation while typing + timeoutId = setTimeout(generatePreview, 1000); return () => { active = false; @@ -79,7 +83,9 @@ export const ReviewPanel: React.FC = ({ useEffect(() => { return () => { setPreviewUrl(prev => { - if (prev && prev.startsWith('blob:')) URL.revokeObjectURL(prev); + if (prev && prev.startsWith('blob:') && prev !== formFile.previewUrl) { + URL.revokeObjectURL(prev); + } return null; }); }; @@ -261,24 +267,32 @@ export const ReviewPanel: React.FC = ({
- PDF Preview + PDF Preview (Live)
{formFile.file.name}
{previewUrl ? ( formFile.type === 'application/pdf' ? ( - -
-

Unable to display PDF directly.

- Download to view + <> +