import { useState, useEffect } from 'react'; import { X, ExternalLink, Box, Code2, Loader2 } from 'lucide-react'; import { ModelViewer } from './ModelViewer'; import { GcodeViewer } from './GcodeViewer'; import { Button } from './Button'; import { api } from '../api/client'; type ViewTab = '3d' | 'gcode'; interface ModelViewerModalProps { archiveId: number; title: string; onClose: () => void; } interface Capabilities { has_model: boolean; has_gcode: boolean; build_volume: { x: number; y: number; z: number }; filament_colors: string[]; } export function ModelViewerModal({ archiveId, title, onClose }: ModelViewerModalProps) { const [activeTab, setActiveTab] = useState(null); const [capabilities, setCapabilities] = useState(null); const [loading, setLoading] = useState(true); // Close on Escape key useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { if (e.key === 'Escape') onClose(); }; window.addEventListener('keydown', handleKeyDown); return () => window.removeEventListener('keydown', handleKeyDown); }, [onClose]); useEffect(() => { api.getArchiveCapabilities(archiveId) .then(caps => { setCapabilities(caps); // Auto-select the first available tab if (caps.has_model) { setActiveTab('3d'); } else if (caps.has_gcode) { setActiveTab('gcode'); } setLoading(false); }) .catch(() => { // Fallback to 3D model tab if capabilities check fails setCapabilities({ has_model: true, has_gcode: false, build_volume: { x: 256, y: 256, z: 256 }, filament_colors: [] }); setActiveTab('3d'); setLoading(false); }); }, [archiveId]); const handleOpenInSlicer = () => { // Use bambustudioopen:// protocol like MakerWorld does // URL must include .3mf filename for Bambu Studio to recognize the format const filename = title || 'model'; const downloadUrl = `${window.location.origin}${api.getArchiveForSlicer(archiveId, filename)}`; window.location.href = `bambustudioopen://${encodeURIComponent(downloadUrl)}`; }; return (
e.stopPropagation()} > {/* Header */}

{title}

{/* Tabs - only show if we have capabilities */} {capabilities && (
)} {/* Viewer */}
{loading ? (
) : activeTab === '3d' && capabilities ? ( ) : activeTab === 'gcode' && capabilities ? ( ) : (
No preview available for this file
)}
); }