setPreview(null)}
/>
)}
{/* Project header */}
Project Name
{editName ? (
setNameVal(e.target.value)}
onBlur={()=>{ setProjectName(nameVal); setEditName(false) }}
onKeyDown={e=>{ if(e.key==='Enter'||e.key==='Escape'){ setProjectName(nameVal); setEditName(false) }}}
autoFocus style={{ fontSize:15, fontWeight:700, width:'100%' }}/>
) : (
{projectName}
{ setNameVal(projectName); setEditName(true) }}
style={{ background:'none', border:'none', color:'var(--text2)', cursor:'pointer', fontSize:14, padding:'2px 4px' }}>
✏️
)}
{models.length} model{models.length!==1?'s':''}
{kfCount} keyframe{kfCount!==1?'s':''}
{cameras.length} cam{cameras.length!==1?'s':''}
{duration} · {fps}fps
{/* ── Auto-save ── */}
Auto-save
{autoSave ? `Saves every 60s · Last: ${lastSaved||'not yet'}` : 'Saves to browser every 60 seconds'}
setAutoSave(v=>!v)} style={{
width:40, height:22, borderRadius:11, border:'none', cursor:'pointer',
background: autoSave ? 'var(--accent3)' : 'var(--bg4)',
position:'relative', transition:'background 0.2s', flexShrink:0,
boxShadow: autoSave ? '0 0 8px rgba(6,214,160,0.4)' : 'none',
}}>
{/* Browser save */}
{/* Quick export */}
{/* Bundle export */}
0 ? ` · Est. ~${fmtBytes(estimatedSize)}` : ''}`}
onClick={handleBundle}
disabled={!hasModels || exporting}
loading={exporting}
badge={skipModels.size > 0 ? `${models.length - skipModels.size}/${models.length} models` : undefined}
/>
{/* Per-model embed toggle */}
{hasModels && (
setShowSkipUI(v=>!v)} style={{
width:'100%', marginTop:4, padding:'5px 10px',
background:'transparent', border:'1px solid var(--border)',
borderRadius:'var(--radius-sm)', color:'var(--text2)',
fontSize:10, cursor:'pointer', textAlign:'left', transition:'all 0.12s',
}}>
{showSkipUI ? '▲ Hide' : '▼ Configure'} which models to embed
)}
{showSkipUI && (
{models.map(m => {
const skip = skipModels.has(m.id)
const isLocal = m.url?.startsWith('blob:') || m.url?.startsWith('data:')
return (
setSkipModels(prev => {
const n = new Set(prev)
if (n.has(m.id)) n.delete(m.id); else n.add(m.id)
return n
})}
style={{ accentColor:'var(--accent2)', width:14, height:14 }}
/>
{m.name}
{isLocal ? '📁 local' : '🔗 url'}
)
})}
)}
{/* Progress bar */}
{exporting && (
{progress.msg}
{progress.pct}%
)}
{/* Share link */}
{/* Drop zone */}
{ e.preventDefault(); setDragging(false); handleFile(e.dataTransfer.files[0]) }}
onDragOver={e=>{ 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:'22px 16px',
textAlign:'center', cursor:'pointer',
background: dragging ? 'rgba(79,142,255,0.06)' : 'var(--bg2)',
transition:'all 0.15s',
}}>
{dragging ? '📂' : '📥'}
{dragging ? 'Drop to preview & import' : 'Drop .glbstudio here'}
or click to browse · Preview shown before loading
{ handleFile(e.target.files[0]); e.target.value='' }}/>
{/* Recent projects */}
{recent.length > 0 && (
setShowRecent(v=>!v)} style={{
width:'100%', padding:'7px 10px', background:'transparent',
border:'1px solid var(--border)', borderRadius:'var(--radius-sm)',
color:'var(--text2)', fontSize:11, cursor:'pointer', textAlign:'left',
display:'flex', justifyContent:'space-between', alignItems:'center',
}}>
🕐 Recent projects ({recent.length})
{showRecent?'▲':'▼'}
{showRecent && (
{recent.map((r,i) => (
{r.type==='bundle'?'📦':'📄'}
{r.name}
{fmtDate(r.date)} · {r.models} model{r.models!==1?'s':''}
))}
{ clearRecentProjects?.(); setRecent([]) }}
style={{ width:'100%', padding:'6px', background:'transparent',
border:'none', color:'var(--text3)', cursor:'pointer', fontSize:10 }}>
Clear history
)}
)}
{/* Format guide */}
📋 Format guide
{[
['📄 Quick Export', 'URLs only · small file · needs internet'],
['📦 Full Bundle', 'Models embedded · self-contained · shareable offline'],
['🔗 Share Link', 'URL only · no file · public models only'],
['💾 Browser Save', 'Instant · same device · clears with browser data'],
].map(([k,v])=>(
{k}
— {v}
))}