fix(pdf-viewer): load PDF.js from CDN instead of npm

- Loads PDF.js from CDN to avoid SSR issues
- Component handles loading state while PDF.js initializes
- No more dynamic import errors
This commit is contained in:
2026-03-23 21:54:13 +01:00
parent 76e37e2363
commit 746615e095
2 changed files with 37 additions and 30 deletions
+2 -17
View File
@@ -1,33 +1,18 @@
"use client"; "use client";
import BackToMC from "@/components/mission-control/BackToMC"; import BackToMC from "@/components/mission-control/BackToMC";
import dynamic from "next/dynamic"; import PDFViewerClient from "@/components/mission-control/PDFViewerClient";
// Dynamically import PDFViewer with SSR disabled
const PDFViewer = dynamic(() => import("@/components/mission-control/PDFViewerClient"), {
ssr: false,
loading: () => (
<div className="flex items-center justify-center h-full">
<div className="text-center">
<div className="animate-spin text-6xl mb-4"></div>
<p className="text-slate-400">Loading PDF viewer...</p>
</div>
</div>
),
});
export default function PdfViewerPage() { export default function PdfViewerPage() {
return ( return (
<div className="min-h-screen bg-slate-950 text-white flex flex-col"> <div className="min-h-screen bg-slate-950 text-white flex flex-col">
{/* Header */}
<div className="bg-slate-900 border-b border-slate-800 px-6 py-4 flex-shrink-0"> <div className="bg-slate-900 border-b border-slate-800 px-6 py-4 flex-shrink-0">
<h1 className="text-2xl font-bold text-white">📄 PDF Viewer</h1> <h1 className="text-2xl font-bold text-white">📄 PDF Viewer</h1>
<p className="text-slate-400 text-sm">View and analyze PDF documents</p> <p className="text-slate-400 text-sm">View and analyze PDF documents</p>
</div> </div>
{/* Content */}
<div className="flex-1 overflow-hidden"> <div className="flex-1 overflow-hidden">
<PDFViewer /> <PDFViewerClient />
</div> </div>
</div> </div>
); );
+35 -13
View File
@@ -1,10 +1,12 @@
"use client"; "use client";
import { useState, useRef, useEffect } from "react"; import { useState, useRef, useEffect } from "react";
import * as pdfjsLib from "pdfjs-dist";
// Set worker source - use CDN declare global {
pdfjsLib.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjsLib.version}/pdf.worker.min.js`; interface Window {
pdfjsLib: any;
}
}
export default function PDFViewerClient() { export default function PDFViewerClient() {
const [pdfData, setPdfData] = useState<string | null>(null); const [pdfData, setPdfData] = useState<string | null>(null);
@@ -14,11 +16,27 @@ export default function PDFViewerClient() {
const [numPages, setNumPages] = useState(0); const [numPages, setNumPages] = useState(0);
const [currentPage, setCurrentPage] = useState(1); const [currentPage, setCurrentPage] = useState(1);
const [scale, setScale] = useState(1.5); const [scale, setScale] = useState(1.5);
const [pdfjsLoaded, setPdfjsLoaded] = useState(false);
const canvasRef = useRef<HTMLCanvasElement>(null); const canvasRef = useRef<HTMLCanvasElement>(null);
const pdfDocRef = useRef<any>(null); const pdfDocRef = useRef<any>(null);
useEffect(() => {
// Load PDF.js from CDN
const script = document.createElement("script");
script.src = "https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.min.js";
script.async = true;
script.onload = () => {
window.pdfjsLib = window.pdfjsLib;
setPdfjsLoaded(true);
};
script.onerror = () => {
setError("Failed to load PDF.js library");
};
document.body.appendChild(script);
}, []);
const renderPage = async (pageNum: number) => { const renderPage = async (pageNum: number) => {
if (!pdfDocRef.current || !canvasRef.current) return; if (!pdfDocRef.current || !canvasRef.current || !window.pdfjsLib) return;
try { try {
const page = await pdfDocRef.current.getPage(pageNum); const page = await pdfDocRef.current.getPage(pageNum);
@@ -39,14 +57,14 @@ export default function PDFViewerClient() {
}; };
useEffect(() => { useEffect(() => {
if (pdfDocRef.current && currentPage) { if (pdfDocRef.current && currentPage && pdfjsLoaded) {
renderPage(currentPage); renderPage(currentPage);
} }
}, [currentPage, scale]); }, [currentPage, scale, pdfjsLoaded]);
const handleFileUpload = async (e: React.ChangeEvent<HTMLInputElement>) => { const handleFileUpload = async (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0]; const file = e.target.files?.[0];
if (!file) return; if (!file || !window.pdfjsLib) return;
if (file.type !== "application/pdf") { if (file.type !== "application/pdf") {
setError("Please select a PDF file"); setError("Please select a PDF file");
@@ -60,7 +78,7 @@ export default function PDFViewerClient() {
try { try {
const arrayBuffer = await file.arrayBuffer(); const arrayBuffer = await file.arrayBuffer();
const pdf = await pdfjsLib.getDocument({ data: arrayBuffer }).promise; const pdf = await window.pdfjsLib.getDocument({ data: arrayBuffer }).promise;
pdfDocRef.current = pdf; pdfDocRef.current = pdf;
setNumPages(pdf.numPages); setNumPages(pdf.numPages);
setPdfData("local"); setPdfData("local");
@@ -74,7 +92,7 @@ export default function PDFViewerClient() {
const handleUrlSubmit = async () => { const handleUrlSubmit = async () => {
const input = prompt("Enter PDF URL:"); const input = prompt("Enter PDF URL:");
if (!input) return; if (!input || !window.pdfjsLib) return;
setLoading(true); setLoading(true);
setError(null); setError(null);
@@ -86,7 +104,7 @@ export default function PDFViewerClient() {
const response = await fetch(input); const response = await fetch(input);
const arrayBuffer = await response.arrayBuffer(); const arrayBuffer = await response.arrayBuffer();
const pdf = await pdfjsLib.getDocument({ data: arrayBuffer }).promise; const pdf = await window.pdfjsLib.getDocument({ data: arrayBuffer }).promise;
pdfDocRef.current = pdf; pdfDocRef.current = pdf;
setNumPages(pdf.numPages); setNumPages(pdf.numPages);
setPdfData("url"); setPdfData("url");
@@ -125,21 +143,26 @@ export default function PDFViewerClient() {
accept="application/pdf" accept="application/pdf"
onChange={handleFileUpload} onChange={handleFileUpload}
className="hidden" className="hidden"
disabled={!pdfjsLoaded}
/> />
</label> </label>
<button <button
onClick={handleUrlSubmit} onClick={handleUrlSubmit}
className="bg-slate-700 hover:bg-slate-600 text-white px-4 py-2 rounded-lg text-sm font-medium transition-colors" disabled={!pdfjsLoaded}
className="bg-slate-700 hover:bg-slate-600 disabled:opacity-50 text-white px-4 py-2 rounded-lg text-sm font-medium transition-colors"
> >
🔗 Load from URL 🔗 Load from URL
</button> </button>
{!pdfjsLoaded && (
<span className="text-slate-400 text-sm">Loading PDF.js...</span>
)}
{pdfData && ( {pdfData && (
<> <>
<div className="h-6 w-px bg-slate-600 mx-2" /> <div className="h-6 w-px bg-slate-600 mx-2" />
{/* Page navigation */}
<button <button
onClick={goToPrevPage} onClick={goToPrevPage}
disabled={currentPage <= 1} disabled={currentPage <= 1}
@@ -160,7 +183,6 @@ export default function PDFViewerClient() {
<div className="h-6 w-px bg-slate-600 mx-2" /> <div className="h-6 w-px bg-slate-600 mx-2" />
{/* Zoom controls */}
<button <button
onClick={zoomOut} onClick={zoomOut}
className="bg-slate-700 hover:bg-slate-600 text-white px-3 py-2 rounded-lg text-sm font-medium transition-colors" className="bg-slate-700 hover:bg-slate-600 text-white px-3 py-2 rounded-lg text-sm font-medium transition-colors"