@@ -10,6 +10,7 @@ import {
1010import { computeAssignments } from './lib/materials/assignMaterials' ;
1111import { parseMeshFile } from './lib/mesh/parseMesh' ;
1212import { normalize } from './lib/mesh/vector' ;
13+ import { ACCEPTED_UPLOAD_EXTENSIONS , isMobile , isAcceptedUploadFile } from './lib/upload' ;
1314import { PreviewViewport } from './components/PreviewViewport' ;
1415import { UploadModal } from './components/UploadModal' ;
1516import { mixFilamentsCached } from './lib/preview/mixCache' ;
@@ -259,6 +260,13 @@ export default function App() {
259260 return ;
260261 }
261262
263+ if ( ! isAcceptedUploadFile ( file . name ) ) {
264+ setOriginalMesh ( null ) ;
265+ setError ( 'Unsupported file type. Please choose an STL, OBJ, or 3MF file.' ) ;
266+ setStatus ( 'Import failed.' ) ;
267+ return ;
268+ }
269+
262270 setIsProcessing ( true ) ;
263271 setError ( null ) ;
264272 setStatus ( `Parsing ${ file . name } ...` ) ;
@@ -334,6 +342,21 @@ export default function App() {
334342 // eslint-disable-next-line react-hooks/exhaustive-deps
335343 } , [ ] ) ;
336344
345+ // Load the bundled 3DBenchy sample on demand (Import → "Load 3D Benchy sample").
346+ const loadSample = async ( ) => {
347+ try {
348+ const response = await fetch ( `${ import . meta. env . BASE_URL } 3dbenchy.stl` ) ;
349+ if ( ! response . ok ) {
350+ throw new Error ( `Sample request failed (${ response . status } ).` ) ;
351+ }
352+ const blob = await response . blob ( ) ;
353+ await loadFile ( new File ( [ blob ] , '3dbenchy.stl' , { type : 'model/stl' } ) ) ;
354+ } catch ( caughtError ) {
355+ setError ( caughtError instanceof Error ? caughtError . message : 'The sample could not be loaded.' ) ;
356+ setStatus ( 'Import failed.' ) ;
357+ }
358+ } ;
359+
337360 const handleFileUpload = async ( event : ChangeEvent < HTMLInputElement > ) => {
338361 const file = event . target . files ?. [ 0 ] ;
339362 if ( file ) {
@@ -493,9 +516,12 @@ export default function App() {
493516 < section className = "panel-section" >
494517 < h2 > Import</ h2 >
495518 < label className = "file-drop" >
496- < input type = "file" accept = ".stl,.obj,.3mf,model/stl,model/3mf,text/plain" onChange = { handleFileUpload } disabled = { isProcessing } />
519+ < input type = "file" accept = { isMobile ? '' : ACCEPTED_UPLOAD_EXTENSIONS . join ( ',' ) } onChange = { handleFileUpload } disabled = { isProcessing } />
497520 < span > Choose STL, OBJ, or 3MF - or drop one on the window</ span >
498521 </ label >
522+ < button type = "button" className = "secondary-button" onClick = { loadSample } disabled = { isProcessing } >
523+ Load 3D Benchy sample
524+ </ button >
499525 < p className = { error ? 'status status-error' : 'status' } > { error ?? status } </ p >
500526 </ section >
501527
0 commit comments