import { useState, useEffect, useRef } from 'react' import useStore from '../store/useStore' function Btn({ icon, label, active, onClick, color, title, shortcut, danger }) { const [h, setH] = useState(false) const acc = color || (active ? 'var(--accent)' : null) return ( ) } function Divider() { return
} export default function Toolbar() { const { transformMode, setTransformMode, snapEnabled, setSnapEnabled, lightingPreset, setLightingPreset, isPlaying, setIsPlaying, loopPlayback, setLoopPlayback, selectedModelId, addKeyframe, currentFrame, currentFrame: cf, setCurrentFrame, totalFrames, undo, redo, undoStack, redoStack, showGrid, setShowGrid, showGizmo, setShowGizmo, showCameraObjects, setShowCameraObjects, projectName, setProjectName, saveProject, loadProject, exportProjectJSON, duplicateModel, removeModel, models, } = useStore() const [editName, setEditName] = useState(false) const [nameVal, setNameVal] = useState(projectName) const importRef = useRef() // Global keyboard shortcuts useEffect(() => { const onKey = (e) => { // Skip if typing in an input if (['INPUT','TEXTAREA','SELECT'].includes(e.target.tagName)) return const k = e.code if (k==='KeyG' && !e.ctrlKey) { setTransformMode('translate'); return } if (k==='KeyR' && !e.ctrlKey) { setTransformMode('rotate'); return } if (k==='KeyS' && !e.ctrlKey) { setTransformMode('scale'); return } if (k==='Space') { e.preventDefault(); setIsPlaying(!useStore.getState().isPlaying); return } if (k==='KeyZ' && (e.ctrlKey||e.metaKey) && !e.shiftKey) { e.preventDefault(); undo(); return } if ((k==='KeyZ' && e.shiftKey && (e.ctrlKey||e.metaKey)) || (k==='KeyY' && (e.ctrlKey||e.metaKey))) { e.preventDefault(); redo(); return } if (k==='KeyD' && !e.ctrlKey) { e.preventDefault(); const sel=useStore.getState().selectedModelId; if(sel) duplicateModel(sel); return } if (k==='Delete'||k==='Backspace') { const sel=useStore.getState().selectedModelId; if(sel){ removeModel(sel) }; return } if (k==='KeyL') { setLoopPlayback(!useStore.getState().loopPlayback); return } if (k==='Tab') { e.preventDefault(); setSnapEnabled(!useStore.getState().snapEnabled); return } // Screenshot if (k==='F12') { e.preventDefault(); takeScreenshot(); return } // Save if ((e.ctrlKey||e.metaKey) && k==='KeyS') { e.preventDefault(); const ok=saveProject(); console.log(ok?'Saved':'Save failed'); return } } window.addEventListener('keydown', onKey) return () => window.removeEventListener('keydown', onKey) }, []) const takeScreenshot = () => { const canvas = document.querySelector('canvas') if (!canvas) return const url = canvas.toDataURL('image/png') const a = document.createElement('a') a.href=url; a.download=`screenshot_${Date.now()}.png`; a.click() } const handleImport = (e) => { const file = e.target.files[0] if (!file) return useStore.getState().importProjectJSON(file).then(result => { if (!result?.ok) console.warn('Import failed:', result?.error) }) e.target.value = '' } const tools = [ { id:'translate', icon:'⊹', label:'Move', short:'G' }, { id:'rotate', icon:'↻', label:'Rotate', short:'R' }, { id:'scale', icon:'⤡', label:'Scale', short:'S' }, ] const lights = [ { id:'studio', icon:'◎' }, { id:'outdoor', icon:'◉' }, { id:'dramatic', icon:'◈' }, { id:'neon', icon:'◆' }, ] const canUndo = undoStack?.length > 0 const canRedo = redoStack?.length > 0 return (
{/* Brand / Project name */}
GLB {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:12, fontWeight:600, width:120, padding:'2px 5px' }} /> ) : ( { setNameVal(projectName); setEditName(true) }} style={{ fontSize:12, fontWeight:600, color:'var(--text1)', cursor:'text', maxWidth:120, overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap' }} title="Double-click to rename" >{projectName} )}
{/* Project actions */} { const ok=saveProject(); if(ok) console.log('Saved') }} /> loadProject()} /> importRef.current?.click()} /> {/* Undo / Redo */} {/* Transform */} {tools.map(t => ( setTransformMode(t.id)} /> ))} {/* Snap toggle */} setSnapEnabled(!snapEnabled)} color="var(--warn)" /> {/* Playback */} setCurrentFrame(0)} /> setCurrentFrame(Math.max(0,cf-1))} /> setCurrentFrame(Math.min(totalFrames-1,cf+1))} /> setCurrentFrame(totalFrames-1)} /> setLoopPlayback(!loopPlayback)} color="var(--accent3)" /> {/* Frame counter */}
{String(cf).padStart(4,'0')} /{totalFrames}
{/* Lighting */} {lights.map(l => ( ))}
{/* Selected model actions */} {selectedModelId && <> duplicateModel(selectedModelId)} /> addKeyframe(currentFrame,selectedModelId)} color="var(--warn)" active={!!useStore.getState().keyframes[currentFrame]?.[selectedModelId]} /> { removeModel(selectedModelId) }} /> } {/* Scene visibility toggles */} setShowGrid(!showGrid)} /> setShowGizmo(!showGizmo)} /> setShowCameraObjects(!showCameraObjects)} /> {/* Screenshot */}
) }