update: fix packages

This commit is contained in:
Tom Boullay
2026-04-03 13:45:23 +02:00
parent 7ce3d61110
commit adeca383bc
6 changed files with 253 additions and 338 deletions
+23 -25
View File
@@ -1,21 +1,6 @@
'use client'
import { Suspense } from 'react'
import { Canvas } from '@react-three/fiber'
import { Stage, OrbitControls, useGLTF } from '@react-three/drei'
function Model({ url }: { url: string }) {
const { scene } = useGLTF(url)
return <primitive object={scene} />
}
function Loader() {
return (
<div className="absolute inset-0 flex items-center justify-center">
<div className="w-6 h-6 border-2 border-gray-500 border-t-gray-300 rounded-full animate-spin" />
</div>
)
}
import { useEffect, useState } from 'react'
interface ModelViewerProps {
url: string
@@ -24,8 +9,28 @@ interface ModelViewerProps {
}
export default function ModelViewer({ url, filename, size }: ModelViewerProps) {
const [Scene, setScene] = useState<React.ComponentType<{ url: string }> | null>(null)
useEffect(() => {
let cancel = false
import('./SceneViewer').then((mod) => {
if (!cancel) setScene(() => mod.default)
})
return () => { cancel = true }
}, [])
if (!Scene) {
return (
<div className="w-full h-[450px] bg-black-800 border border-white/20 rounded-xl overflow-hidden flex items-center justify-center">
<div className="w-6 h-6 border-2 border-gray-500 border-t-gray-300 rounded-full animate-spin" />
</div>
)
}
return (
<div className="w-full h-[450px] bg-black-800 border border-black-700 rounded-xl overflow-hidden relative">
<div className="w-full h-[450px] bg-black-800 border border-white/20 rounded-xl overflow-hidden relative">
<div className="absolute top-3 left-3 z-10 flex items-center gap-2">
<span className="text-xs text-gray-400 font-mono bg-black-900/60 px-2 py-1 rounded">
{filename}
@@ -34,14 +39,7 @@ export default function ModelViewer({ url, filename, size }: ModelViewerProps) {
{size}
</span>
</div>
<Canvas shadows dpr={[1, 2]} camera={{ fov: 50 }}>
<Suspense fallback={null}>
<Stage environment="city" intensity={0.6} adjustCamera={1.2}>
<Model url={url} />
</Stage>
</Suspense>
<OrbitControls makeDefault autoRotate autoRotateSpeed={0.5} />
</Canvas>
<Scene url={url} />
</div>
)
}
+23
View File
@@ -0,0 +1,23 @@
'use client'
import { Suspense } from 'react'
import { Canvas } from '@react-three/fiber'
import { Stage, OrbitControls, useGLTF } from '@react-three/drei'
function Model({ url }: { url: string }) {
const { scene } = useGLTF(url)
return <primitive object={scene} />
}
export default function SceneViewer({ url }: { url: string }) {
return (
<Canvas dpr={[1, 2]} camera={{ fov: 50 }}>
<Suspense fallback={null}>
<Stage environment="city" intensity={0.6} adjustCamera={1.2}>
<Model url={url} />
</Stage>
</Suspense>
<OrbitControls makeDefault autoRotate autoRotateSpeed={0.5} />
</Canvas>
)
}
+73 -52
View File
@@ -15,6 +15,7 @@ interface FileEntry {
error?: string
filename?: string
previewUrl?: string
viewerOpen?: boolean
}
interface UploadResult {
@@ -174,7 +175,7 @@ export default function UploadZone() {
const newEntries: FileEntry[] = acceptedFiles
.filter((f) => !existingNames.has(f.name))
.map((file) => {
const entry: FileEntry = { file, status: 'pending', progress: 0 }
const entry: FileEntry = { file, status: 'pending', progress: 0, viewerOpen: true }
const type = getFileType(file.name)
if (type === 'model') {
entry.previewUrl = URL.createObjectURL(file)
@@ -190,7 +191,7 @@ export default function UploadZone() {
accept: ACCEPTED_FORMATS,
maxSize: 2 * 1024 * 1024 * 1024,
disabled: isUploading,
multiple: true,
multiple: false,
})
const allDone = files.length > 0 && files.every((f) => f.status === 'success')
@@ -207,9 +208,9 @@ export default function UploadZone() {
onChange={(e) => setSecret(e.target.value)}
placeholder="Enter secret key..."
disabled={isUploading}
className="w-full bg-black-800 border border-black-700 rounded-xl px-4 py-2.5 pr-12
className="w-full bg-black-800 border border-white/30 rounded-xl px-4 py-2.5 pr-12
text-gray-100 placeholder-gray-500 text-sm
focus:outline-none focus:ring-2 focus:ring-gray-600 focus:border-gray-500
focus:outline-none focus:ring-2 focus:ring-white/50 focus:border-white/50
disabled:opacity-50 disabled:cursor-not-allowed transition"
/>
<button
@@ -243,52 +244,53 @@ export default function UploadZone() {
onChange={(e) => setAssetName(e.target.value)}
placeholder="e.g., tower, stone_floor, brick_wall..."
disabled={isUploading}
className="w-full bg-black-800 border border-black-700 rounded-xl px-4 py-2.5
text-gray-100 placeholder-gray-500 text-sm font-mono
focus:outline-none focus:ring-2 focus:ring-gray-600 focus:border-gray-500
disabled:opacity-50 disabled:cursor-not-allowed transition"
className="w-full bg-black-800 border border-white/30 rounded-xl px-4 py-2.5
text-gray-100 placeholder-gray-500 text-sm font-mono
focus:outline-none focus:ring-2 focus:ring-white/50 focus:border-white/50
disabled:opacity-50 disabled:cursor-not-allowed transition"
/>
</div>
<div
{...getRootProps()}
className={`
relative border-2 border-dashed rounded-2xl p-8 text-center cursor-pointer transition-all duration-200 bg-black-800
${isUploading ? 'cursor-not-allowed opacity-60 border-black-700' : ''}
${isDragReject ? 'border-red-500 bg-red-900/20' : ''}
${isDragActive && !isDragReject ? 'border-gray-400 bg-black-700 scale-[1.01]' : ''}
${!isDragActive && !isDragReject && !isUploading
? 'border-black-600 hover:border-gray-500 hover:bg-black-700'
: ''}
`}
>
<input {...getInputProps()} />
<div className="flex justify-center mb-3">
<div className={`w-12 h-12 rounded-full flex items-center justify-center transition
${isDragActive ? 'bg-gray-700' : 'bg-black-700'}`}>
<svg className={`w-6 h-6 transition ${isDragActive ? 'text-white' : 'text-gray-400'}`}
fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
<path strokeLinecap="round" strokeLinejoin="round"
d="M3 16.5v2.25A2.25 2.25 0 005.25 21h13.5A2.25 2.25 0 0021 18.75V16.5m-13.5-9L12 3m0 0l4.5 4.5M12 3v13.5" />
</svg>
{files.length === 0 && (
<div
{...getRootProps()}
className={`
relative border-2 border-dashed rounded-2xl p-8 text-center cursor-pointer transition-all duration-200 bg-black-800
${isUploading ? 'cursor-not-allowed opacity-60 border-white/20' : ''}
${isDragReject ? 'border-red-500 bg-red-900/20' : ''}
${isDragActive && !isDragReject ? 'border-white/50 bg-black-700 scale-[1.01]' : ''}
${!isDragActive && !isDragReject && !isUploading
? 'border-white/30 hover:border-white/50 hover:bg-black-700'
: ''}
`}
>
<input {...getInputProps()} />
<div className="flex justify-center mb-3">
<div className={`w-12 h-12 rounded-full flex items-center justify-center transition ${isDragActive ? 'bg-gray-700' : 'bg-black-700'}`}>
<svg className={`w-6 h-6 transition ${isDragActive ? 'text-white' : 'text-gray-400'}`}
fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
<path strokeLinecap="round" strokeLinejoin="round"
d="M3 16.5v2.25A2.25 2.25 0 005.25 21h13.5A2.25 2.25 0 0021 18.75V16.5m-13.5-9L12 3m0 0l4.5 4.5M12 3v13.5" />
</svg>
</div>
</div>
{isDragReject ? (
<p className="text-sm font-medium text-red-400">Unsupported format</p>
) : isDragActive ? (
<p className="text-sm font-medium text-gray-200">Release to add</p>
) : (
<>
<p className="text-sm font-medium text-gray-300">
Drag files here
<span className="text-gray-500 font-normal"> or click to browse</span>
</p>
<p className="text-xs text-gray-500 mt-1">
Models: .glb, .gltf · Textures: .png, .jpg, .webp
</p>
</>
)}
</div>
{isDragReject ? (
<p className="text-sm font-medium text-red-400">Unsupported format</p>
) : isDragActive ? (
<p className="text-sm font-medium text-gray-200">Release to add</p>
) : (
<>
<p className="text-sm font-medium text-gray-300">
Drag files here
<span className="text-gray-500 font-normal"> or click to browse</span>
</p>
<p className="text-xs text-gray-500 mt-1">
Models: .glb, .gltf · Textures: .png, .jpg, .webp
</p>
</>
)}
</div>
)}
{globalError && (
<p className="text-xs text-red-400 text-center">{globalError}</p>
@@ -300,7 +302,7 @@ export default function UploadZone() {
const type = getFileType(entry.file.name)
return (
<div key={`${entry.file.name}-${i}`}>
<div className="flex items-center gap-3 bg-black-800 border border-black-700 rounded-xl px-4 py-3">
<div className="flex items-center gap-3 bg-black-800 border border-white/20 rounded-xl px-4 py-3">
<div className="shrink-0">
{entry.status === 'success' ? (
@@ -323,11 +325,25 @@ export default function UploadZone() {
</svg>
</div>
) : (
<div className="w-8 h-8 rounded-full bg-black-700 flex items-center justify-center">
<svg className="w-4 h-4 text-gray-500" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
<button
onClick={() => {
if (entry.previewUrl && type === 'model') {
updateFile(i, { viewerOpen: !entry.viewerOpen })
}
}}
className={`shrink-0 w-8 h-8 rounded-full flex items-center justify-center transition ${
entry.previewUrl && type === 'model'
? 'bg-black-700 hover:bg-gray-700 cursor-pointer'
: 'bg-black-700 cursor-default'
}`}
>
<svg
className={`w-4 h-4 text-gray-500 transition-transform ${entry.viewerOpen ? 'rotate-180' : ''}`}
fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}
>
<path strokeLinecap="round" strokeLinejoin="round" d="M19 9l-7 7-7-7" />
</svg>
</div>
</button>
)}
</div>
@@ -373,7 +389,11 @@ export default function UploadZone() {
</div>
{entry.previewUrl && type === 'model' && entry.status !== 'success' && (
<div className="mt-2">
<div
className={`transition-all duration-300 ease-in-out ${
entry.viewerOpen ? 'max-h-[500px] opacity-100 mt-2' : 'max-h-0 opacity-0'
}`}
>
<ModelViewer
url={entry.previewUrl}
filename={entry.file.name}
@@ -392,8 +412,9 @@ export default function UploadZone() {
<button
onClick={handleUpload}
className="flex-1 bg-white text-black font-medium text-sm
py-2.5 px-6 rounded-xl transition-opacity duration-150
hover:bg-gray-200 focus:outline-none focus:ring-2 focus:ring-gray-400"
py-2.5 px-6 rounded-xl transition-all duration-150
hover:bg-gray-200 focus:outline-none focus:ring-2 focus:ring-white/50 border border-white/20"
style={{ color: '#000000' }}
>
Upload {files.filter(f => f.status !== 'success').length > 1
? `${files.filter(f => f.status !== 'success').length} files`