diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 5ad88a7..116b4e4 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -20,6 +20,7 @@ "react": "^19.0.0", "react-19-stl-viewer": "^3.0.1", "react-dom": "^19.0.0", + "react-intersection-observer": "^10.0.3", "react-pdf": "^10.4.1", "react-syntax-highlighter": "^16.1.1", "three": "^0.184.0", @@ -3025,6 +3026,21 @@ "react": "^19.2.6" } }, + "node_modules/react-intersection-observer": { + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/react-intersection-observer/-/react-intersection-observer-10.0.3.tgz", + "integrity": "sha512-luICLMbs0zxTO/70Zy7K5jOXkABPEVSAF8T3FdZUlctsrIaPLmx8TZe2SSA+CY2HGWfz2INyNTnp82pxNNsShA==", + "license": "MIT", + "peerDependencies": { + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, "node_modules/react-pdf": { "version": "10.4.1", "resolved": "https://registry.npmjs.org/react-pdf/-/react-pdf-10.4.1.tgz", diff --git a/frontend/package.json b/frontend/package.json index 1d457ba..98ae7dd 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -21,6 +21,7 @@ "react": "^19.0.0", "react-19-stl-viewer": "^3.0.1", "react-dom": "^19.0.0", + "react-intersection-observer": "^10.0.3", "react-pdf": "^10.4.1", "react-syntax-highlighter": "^16.1.1", "three": "^0.184.0", diff --git a/frontend/src/components/FileExplorer.tsx b/frontend/src/components/FileExplorer.tsx index f064d5f..1982cab 100644 --- a/frontend/src/components/FileExplorer.tsx +++ b/frontend/src/components/FileExplorer.tsx @@ -1159,7 +1159,7 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) { const ext = file.name.split('.').pop()?.toLowerCase() || ''; const isImage = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg'].includes(ext) || file.mime_type?.startsWith('image/'); const isVideo = ['mp4', 'mkv', 'avi', 'mov', 'webm', 'ogg'].includes(ext) || file.mime_type?.startsWith('video/'); - const isOffice = ['docx', 'doc', 'pptx', 'ppt', 'xlsx', 'xls'].includes(ext) || file.mime_type?.includes('officedocument'); + const isOffice = ['docx', 'doc', 'pptx', 'ppt', 'xlsx', 'xls', 'pdf'].includes(ext) || file.mime_type?.includes('officedocument') || file.mime_type === 'application/pdf'; const isAudio = ['mp3', 'flac', 'wav', 'm4a', 'wma', 'aac'].includes(ext) || file.mime_type?.startsWith('audio/'); const isMedia = isImage || isVideo || isOffice || isAudio; @@ -1240,7 +1240,7 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) { })(); if (isMedia && file.checksum && viewMode === 'grid') { - const docType = file.name.endsWith('.docx') ? 'docs' : file.name.endsWith('.pptx') ? 'slides' : (file.name.endsWith('.xlsx') || file.name.endsWith('.xls')) ? 'sheets' : null; + const docType = (file.name.endsWith('.docx') || file.name.endsWith('.pdf')) ? 'docs' : file.name.endsWith('.pptx') ? 'slides' : (file.name.endsWith('.xlsx') || file.name.endsWith('.xls')) ? 'sheets' : null; return (
diff --git a/frontend/src/components/PdfViewer.tsx b/frontend/src/components/PdfViewer.tsx index 56735ff..e9f4203 100644 --- a/frontend/src/components/PdfViewer.tsx +++ b/frontend/src/components/PdfViewer.tsx @@ -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 ( +
+ {inView ? ( + + ) : ( +
+ )} +
+ ); +} + export default function PdfViewer({ url }: { url: string }) { const [numPages, setNumPages] = useState(); - const [pageNumber, setPageNumber] = useState(1); const [scale, setScale] = useState(1.2); function onDocumentLoadSuccess({ numPages }: { numPages: number }): void { @@ -24,25 +47,9 @@ export default function PdfViewer({ url }: { url: string }) { {/* PDF Toolbar */}
- - {numPages ? `${pageNumber} / ${numPages}` : 'Loading...'} + {numPages ? `${numPages} Pages` : 'Loading...'} -
} - className="flex flex-col items-center gap-6" + className="flex flex-col items-center gap-2 w-full" > - {numPages && ( - - )} + {numPages && Array.from(new Array(numPages), (el, index) => ( + + ))}
diff --git a/frontend/src/components/ThreeMfPreview.tsx b/frontend/src/components/ThreeMfPreview.tsx index f31f024..f10834c 100644 --- a/frontend/src/components/ThreeMfPreview.tsx +++ b/frontend/src/components/ThreeMfPreview.tsx @@ -1,6 +1,6 @@ import React, { useState, useEffect } from 'react'; import { Canvas } from '@react-three/fiber'; -import { OrbitControls, Stage } from '@react-three/drei'; +import { OrbitControls, Center } from '@react-three/drei'; import { ThreeMFLoader } from 'three-stdlib'; import { Loader2 } from 'lucide-react'; import * as THREE from 'three'; @@ -64,8 +64,11 @@ export default function ThreeMfPreview({ url }: ThreeMfPreviewProps) {
+ + + - +
setLoading(false)} @@ -75,7 +78,7 @@ export default function ThreeMfPreview({ url }: ThreeMfPreviewProps) { setLoading(false); }} /> - +