Spaces:
Sleeping
Sleeping
| "use client"; | |
| import { useState, useEffect } from 'react'; | |
| export default function PdfViewer({ pdfUrl, pageNumber, searchTerm }) { | |
| const [loading, setLoading] = useState(true); | |
| const [error, setError] = useState(false); | |
| // Reset loading state when URL or page changes | |
| useEffect(() => { | |
| setLoading(true); | |
| setError(false); | |
| }, [pdfUrl, pageNumber]); | |
| if (!pdfUrl) { | |
| return ( | |
| <div className="pdf-placeholder"> | |
| <p>No PDF available for this document.</p> | |
| </div> | |
| ); | |
| } | |
| // PDF pages in our data are 0-indexed; PDF.js viewer expects 1-indexed pages | |
| const viewerPage = (pageNumber ?? 0) + 1; | |
| // Proxy/Route the PDF through our local PDF.js viewer to support highlighting and bypass CORS. | |
| const isLocal = pdfUrl.startsWith('/') || pdfUrl.includes('localhost') || pdfUrl.includes('127.0.0.1'); | |
| const viewerPdfUrl = isLocal ? pdfUrl : `/api/pdf-proxy?url=${encodeURIComponent(pdfUrl)}`; | |
| const proxyUrl = `/pdfjs/web/viewer.html?file=${encodeURIComponent(viewerPdfUrl)}#page=${viewerPage}${searchTerm ? `&search=${encodeURIComponent(searchTerm)}&phrase=true` : ''}`; | |
| return ( | |
| <div className="pdf-container"> | |
| {loading && !error && ( | |
| <div className="pdf-loading"> | |
| <div className="loading-spinner" /> | |
| <p>Loading PDF...</p> | |
| <p className="pdf-loading-hint">This may take a moment for large documents.</p> | |
| </div> | |
| )} | |
| {error && ( | |
| <div className="pdf-error"> | |
| <p>⚠️ Failed to load PDF</p> | |
| <p className="pdf-error-hint">The document may be unavailable or too large.</p> | |
| <a href={pdfUrl} target="_blank" rel="noopener noreferrer" className="btn btn-secondary"> | |
| Open PDF directly ↗ | |
| </a> | |
| </div> | |
| )} | |
| <iframe | |
| key={`pdf-${pdfUrl}-page-${viewerPage}`} | |
| src={proxyUrl} | |
| className="pdf-frame" | |
| title={`PDF Page ${viewerPage}`} | |
| allow="fullscreen" | |
| style={{ display: loading && !error ? 'none' : 'block' }} | |
| onLoad={() => setLoading(false)} | |
| onError={() => { setLoading(false); setError(true); }} | |
| /> | |
| </div> | |
| ); | |
| } | |