import { useState, useRef } from 'react'
import useStore from '../store/useStore'
const SAMPLE_CATEGORIES = [
{
label: '🚗 Vehicles',
items: [
{ name: 'Race Car', url: 'https://vazxmixjsiawhamofees.supabase.co/storage/v1/object/public/models/car-sport/model.gltf' },
{ name: 'Low Poly Car',url: 'https://market-assets.fra1.cdn.digitaloceanspaces.com/market-assets/models/car-with-2-doors/model.gltf' },
{ name: 'Truck', url: 'https://market-assets.fra1.cdn.digitaloceanspaces.com/market-assets/models/truck/model.gltf' },
]
},
{
label: '🏙️ City / Environment',
items: [
{ name: 'City Block', url: 'https://market-assets.fra1.cdn.digitaloceanspaces.com/market-assets/models/city-buildings/model.gltf' },
{ name: 'Tree', url: 'https://market-assets.fra1.cdn.digitaloceanspaces.com/market-assets/models/tree-spruce/model.gltf' },
{ name: 'Bench', url: 'https://market-assets.fra1.cdn.digitaloceanspaces.com/market-assets/models/bench/model.gltf' },
]
},
{
label: '🦊 Characters',
items: [
{ name: 'Fox', url: 'https://threejs.org/examples/models/gltf/Fox/glTF/Fox.gltf' },
{ name: 'Robot', url: 'https://threejs.org/examples/models/gltf/RobotExpressive/RobotExpressive.glb' },
{ name: 'Soldier', url: 'https://threejs.org/examples/models/gltf/Soldier.glb' },
{ name: 'Flamingo', url: 'https://threejs.org/examples/models/gltf/Flamingo.glb' },
]
},
]
// Flatten for backwards compat
const SAMPLES = SAMPLE_CATEGORIES.flatMap(c => c.items)
const COLORS = ['#4f8eff','#ef4444','#06d6a0','#f59e0b','#8b5cf6','#f97316']
export default function ModelsPanel() {
const { models, selectedModelId, addModel, removeModel, selectModel, toggleModelVisibility } = useStore()
const [url, setUrl] = useState('')
const [name, setName] = useState('')
const [samples, setSamples] = useState(false)
const [dragging, setDragging] = useState(false)
const fileRef = useRef()
const handleAdd = () => {
if (!url.trim()) return
addModel(url.trim(), name.trim() || null)
setUrl(''); setName('')
}
const handleFile = (e) => {
const file = e.target.files[0]
if (!file) return
addModel(URL.createObjectURL(file), file.name.replace(/\.[^.]+$/, ''))
}
const handleDrop = (e) => {
e.preventDefault(); setDragging(false)
const file = e.dataTransfer.files[0]
if (file && (file.name.endsWith('.glb') || file.name.endsWith('.gltf'))) {
addModel(URL.createObjectURL(file), file.name.replace(/\.[^.]+$/, ''))
}
}
return (
{/* Drop zone */}
{ e.preventDefault(); setDragging(true) }}
onDragLeave={() => setDragging(false)}
onClick={() => fileRef.current?.click()}
style={{
border: `2px dashed ${dragging ? 'var(--accent)' : 'var(--border-hi)'}`,
borderRadius: 'var(--radius)',
padding: '20px 12px',
textAlign: 'center',
cursor: 'pointer',
background: dragging ? 'rgba(79,142,255,0.06)' : 'var(--bg2)',
transition: 'all 0.15s',
animation: dragging ? 'pulse 1s ease infinite' : 'none',
}}
>
📦
{dragging ? 'Drop to load' : 'Drop GLB / GLTF here'}
or click to browse
{/* URL input */}
{/* Sample models */}
{samples && (
{SAMPLE_CATEGORIES.map(cat => (
{cat.label}
{cat.items.map(item => (
))}
))}
)}
{/* Divider + count */}
{models.length > 0 && (
{models.length} MODEL{models.length>1?'S':''}
)}
{/* Model list */}
{models.map((m, i) => {
const sel = m.id === selectedModelId
const c = COLORS[i % COLORS.length]
return (
selectModel(m.id)}
style={{
display:'flex', alignItems:'center', gap:8,
padding:'8px 10px',
borderRadius:'var(--radius-sm)',
background: sel ? 'rgba(79,142,255,0.1)' : 'var(--bg2)',
border:`1px solid ${sel ? 'rgba(79,142,255,0.3)' : 'var(--border)'}`,
cursor:'pointer', transition:'all 0.12s',
}}
onMouseEnter={e => { if (!sel) e.currentTarget.style.background='var(--bg3)' }}
onMouseLeave={e => { if (!sel) e.currentTarget.style.background='var(--bg2)' }}
>
{/* Color dot */}
{/* Name */}
{m.name}
{/* Anims badge */}
{(m.animations?.length??0)>0 && (
{m.animations.length}
)}
{/* Actions */}
)
})}
{models.length === 0 && (
No models loaded yet
)}
)
}