Add STL 3D viewer preview support
All checks were successful
Automated Container Build / build-and-push (push) Successful in 29s

This commit is contained in:
Elijah 2026-05-31 21:21:57 -07:00
parent bfe55e2bfc
commit cd540499ca
4 changed files with 462 additions and 2 deletions

View file

@ -10,6 +10,7 @@ import { vscDarkPlus } from 'react-syntax-highlighter/dist/esm/styles/prism';
import { formatSize, getFileDisplayName } from '@/lib/utils';
const DynamicPdfViewer = dynamic(() => import('./PdfViewer'), { ssr: false });
const DynamicStlPreview = dynamic(() => import('./StlPreview'), { ssr: false });
interface FilePreviewProps {
file: {
@ -85,7 +86,7 @@ export default function FilePreview({ file, onClose, downloadToken }: FilePrevie
exit={{ scale: 0.95, opacity: 0, y: 20 }}
onClick={(e) => e.stopPropagation()}
className={`relative flex flex-col max-w-[95vw] max-h-[95vh] rounded-2xl overflow-hidden shadow-2xl ${
(previewType === 'image' || previewType === 'video' || previewType === 'pdf' || previewType === 'audio') ? 'w-fit' : 'w-full'
(previewType === 'image' || previewType === 'video' || previewType === 'pdf' || previewType === 'stl' || previewType === 'audio') ? 'w-fit' : 'w-full'
} ${
(previewType === 'image' || previewType === 'video' || previewType === 'audio') ? 'h-fit' : 'h-full'
}`}
@ -160,6 +161,16 @@ export default function FilePreview({ file, onClose, downloadToken }: FilePrevie
)
)}
{previewType === 'stl' && (
!downloadToken ? (
<div className="flex items-center justify-center p-8"><Loader2 className="w-8 h-8 animate-spin text-accent" /></div>
) : (
<div className="w-[80vw] h-[80vh] max-w-[1200px] max-h-[800px]">
<DynamicStlPreview url={downloadUrl} />
</div>
)
)}
{(previewType === 'text' || previewType === 'code') && (
<div className="absolute inset-0 p-4">
{loading ? (
@ -234,12 +245,13 @@ export default function FilePreview({ file, onClose, downloadToken }: FilePrevie
// --- Helpers ---
type PreviewType = 'image' | 'video' | 'audio' | 'text' | 'code' | 'pdf' | 'unsupported';
type PreviewType = 'image' | 'video' | 'audio' | 'text' | 'code' | 'pdf' | 'stl' | 'unsupported';
function getPreviewType(file: { name: string; mime_type: string }): PreviewType {
const ext = file.name.split('.').pop()?.toLowerCase() || '';
if (ext === 'pdf' || file.mime_type === 'application/pdf') return 'pdf';
if (ext === 'stl' || file.mime_type === 'model/stl') return 'stl';
if (['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg', 'bmp'].includes(ext)) return 'image';
if (['mp4', 'webm', 'ogg'].includes(ext)) return 'video'; // Only browser-supported videos
if (['mp3', 'wav', 'flac', 'm4a', 'aac'].includes(ext)) return 'audio';