Fix PDF thumbnails, 3MF rendering, and add PDF infinite scroll
All checks were successful
Automated Container Build / build-and-push (push) Successful in 32s

This commit is contained in:
Elijah 2026-05-31 21:43:08 -07:00
parent 9b0e60109f
commit facb1c6f8b
5 changed files with 55 additions and 35 deletions

View file

@ -2,7 +2,8 @@
import { useState } from 'react';
import { Document, Page, pdfjs } from 'react-pdf';
import { ChevronLeft, ChevronRight, ZoomIn, ZoomOut, Loader2 } from 'lucide-react';
import { ZoomIn, ZoomOut, Loader2 } from 'lucide-react';
import { useInView } from 'react-intersection-observer';
import 'react-pdf/dist/Page/AnnotationLayer.css';
import 'react-pdf/dist/Page/TextLayer.css';
@ -10,9 +11,31 @@ if (typeof window !== 'undefined') {
pdfjs.GlobalWorkerOptions.workerSrc = `//unpkg.com/pdfjs-dist@${pdfjs.version}/build/pdf.worker.min.mjs`;
}
function VirtualPage({ pageNumber, scale }: { pageNumber: number; scale: number }) {
const { ref, inView } = useInView({
triggerOnce: false,
rootMargin: '200% 0px', // load pages 2 screens ahead/behind
});
return (
<div ref={ref} className="min-h-[800px] flex justify-center mb-6 w-full" style={{ minHeight: `${800 * scale}px` }}>
{inView ? (
<Page
pageNumber={pageNumber}
scale={scale}
renderTextLayer={true}
renderAnnotationLayer={true}
className="bg-white shadow-xl"
/>
) : (
<div className="bg-white shadow-xl w-[600px]" style={{ width: `${600 * scale}px` }} />
)}
</div>
);
}
export default function PdfViewer({ url }: { url: string }) {
const [numPages, setNumPages] = useState<number>();
const [pageNumber, setPageNumber] = useState<number>(1);
const [scale, setScale] = useState<number>(1.2);
function onDocumentLoadSuccess({ numPages }: { numPages: number }): void {
@ -24,25 +47,9 @@ export default function PdfViewer({ url }: { url: string }) {
{/* PDF Toolbar */}
<div className="flex items-center justify-between p-2 border-b" style={{ borderColor: 'var(--color-border-subtle)', backgroundColor: 'var(--color-bg-elevated)' }}>
<div className="flex items-center gap-2">
<button
onClick={() => setPageNumber(p => Math.max(1, p - 1))}
disabled={pageNumber <= 1}
className="p-1 rounded hover:bg-white/10 disabled:opacity-50"
style={{ color: 'var(--color-text-primary)' }}
>
<ChevronLeft className="w-5 h-5" />
</button>
<span className="text-sm font-medium min-w-[80px] text-center" style={{ color: 'var(--color-text-secondary)' }}>
{numPages ? `${pageNumber} / ${numPages}` : 'Loading...'}
{numPages ? `${numPages} Pages` : 'Loading...'}
</span>
<button
onClick={() => setPageNumber(p => (numPages ? Math.min(numPages, p + 1) : p))}
disabled={pageNumber >= (numPages || 1)}
className="p-1 rounded hover:bg-white/10 disabled:opacity-50"
style={{ color: 'var(--color-text-primary)' }}
>
<ChevronRight className="w-5 h-5" />
</button>
</div>
<div className="flex items-center gap-2">
<button
@ -76,18 +83,11 @@ export default function PdfViewer({ url }: { url: string }) {
<Loader2 className="w-8 h-8 animate-spin" style={{ color: 'var(--color-accent)' }} />
</div>
}
className="flex flex-col items-center gap-6"
className="flex flex-col items-center gap-2 w-full"
>
{numPages && (
<Page
key={`page_${pageNumber}`}
pageNumber={pageNumber}
scale={scale}
renderTextLayer={true}
renderAnnotationLayer={true}
className="bg-white shadow-xl"
/>
)}
{numPages && Array.from(new Array(numPages), (el, index) => (
<VirtualPage key={`page_${index + 1}`} pageNumber={index + 1} scale={scale} />
))}
</Document>
</div>
</div>