feat: Add interaction toolbar and block count HUD to Map3D component

This commit is contained in:
MayaTheShy
2026-02-20 02:19:51 -05:00
parent be0064aae5
commit a64e9a1a51

View File

@@ -1282,16 +1282,133 @@ function Scene({ interactionMode, onInteraction }) {
);
}
// Interaction mode toolbar overlay
function InteractionToolbar({ mode, setMode, lastInteraction }) {
const modes = [
{ key: INTERACTION_MODE.LOOK, icon: '👁️', label: 'Look', desc: 'Orbit camera' },
{ key: INTERACTION_MODE.MOVE, icon: '🎯', label: 'Move', desc: 'Click to send turtle' },
{ key: INTERACTION_MODE.BUILD, icon: '🧱', label: 'Build', desc: 'Click face to preview' },
{ key: INTERACTION_MODE.SELECT, icon: '⬜', label: 'Select', desc: 'Click two corners' },
];
return (
<div style={{
position: 'absolute',
bottom: '16px',
left: '50%',
transform: 'translateX(-50%)',
display: 'flex',
gap: '4px',
background: 'rgba(15, 23, 42, 0.9)',
padding: '6px',
borderRadius: '12px',
border: '1px solid rgba(100, 116, 139, 0.3)',
zIndex: 10,
backdropFilter: 'blur(10px)',
}}>
{modes.map(m => (
<button
key={m.key}
onClick={() => setMode(m.key)}
title={m.desc}
style={{
padding: '8px 14px',
borderRadius: '8px',
border: mode === m.key ? '2px solid #3b82f6' : '1px solid transparent',
background: mode === m.key ? 'rgba(59, 130, 246, 0.2)' : 'rgba(30, 41, 59, 0.8)',
color: mode === m.key ? '#60a5fa' : '#94a3b8',
cursor: 'pointer',
fontSize: '13px',
display: 'flex',
alignItems: 'center',
gap: '6px',
transition: 'all 0.15s ease',
}}
>
<span style={{ fontSize: '16px' }}>{m.icon}</span>
{m.label}
</button>
))}
{lastInteraction && (
<div style={{
position: 'absolute',
bottom: '100%',
left: '50%',
transform: 'translateX(-50%)',
marginBottom: '8px',
padding: '6px 12px',
background: 'rgba(15, 23, 42, 0.95)',
borderRadius: '8px',
border: '1px solid rgba(100, 116, 139, 0.3)',
color: '#94a3b8',
fontSize: '12px',
whiteSpace: 'nowrap',
}}>
{lastInteraction.mode === 'move' && `🎯 Moving turtle to (${lastInteraction.target.x}, ${lastInteraction.target.y}, ${lastInteraction.target.z})`}
{lastInteraction.mode === 'build' && `🧱 Build at (${lastInteraction.target.x}, ${lastInteraction.target.y}, ${lastInteraction.target.z})`}
{lastInteraction.mode === 'select' && lastInteraction.end &&
`⬜ Selected (${lastInteraction.start.x},${lastInteraction.start.y},${lastInteraction.start.z}) → (${lastInteraction.end.x},${lastInteraction.end.y},${lastInteraction.end.z})`}
</div>
)}
</div>
);
}
// Block count HUD
function BlockCountHUD({ blocks }) {
const count = blocks.length;
const chunkCount = useMemo(() => {
const chunks = new Set();
blocks.forEach(b => chunks.add(`${Math.floor(b.x / 16)},${Math.floor(b.z / 16)}`));
return chunks.size;
}, [blocks]);
return (
<div style={{
position: 'absolute',
top: '12px',
right: '12px',
padding: '8px 12px',
background: 'rgba(15, 23, 42, 0.85)',
borderRadius: '8px',
border: '1px solid rgba(100, 116, 139, 0.2)',
color: '#94a3b8',
fontSize: '12px',
zIndex: 10,
}}>
🧊 {count.toLocaleString()} blocks · 📦 {chunkCount} chunks
</div>
);
}
// Main Map3D component
export default function Map3D() {
const [interactionMode, setInteractionMode] = useState(INTERACTION_MODE.LOOK);
const [lastInteraction, setLastInteraction] = useState(null);
const worldBlocks = useTurtleStore((state) => state.worldBlocks || []);
const handleInteraction = useCallback((event) => {
setLastInteraction(event);
// Clear notification after 5 seconds
setTimeout(() => setLastInteraction(null), 5000);
}, []);
return (
<div style={{ width: '100%', height: '100%', background: '#0a0e1a' }}>
<div style={{ width: '100%', height: '100%', background: '#0a0e1a', position: 'relative' }}>
<Canvas
camera={{ position: [15, 15, 15], fov: 60 }}
style={{ background: '#0a0e1a' }}
>
<Scene />
<Scene interactionMode={interactionMode} onInteraction={handleInteraction} />
</Canvas>
<BlockCountHUD blocks={worldBlocks} />
<InteractionToolbar
mode={interactionMode}
setMode={setInteractionMode}
lastInteraction={lastInteraction}
/>
</div>
);
}