import { useStore } from '@nanostores/react'; import { useState } from 'react'; import { $appSettings, updateAppSettings } from '../stores/appSettings'; import { VALUE_MODES, ValueMode, RENDER_MODES } from '../utils/constants'; import { uiState, toggleMobileMenu, showHelp, toggleUI, toggleShaderLibrary, } from '../stores/ui'; import { $shader, setShaderCode } from '../stores/shader'; import { $input } from '../stores/input'; import { FakeShader } from '../FakeShader'; import { useAudio } from '../hooks/useAudio'; import { LucideIcon } from '../hooks/useLucideIcon'; function getValueModeLabel(mode: string): string { // Automatically generate human-readable labels from mode names return mode.charAt(0).toUpperCase() + mode.slice(1).replace(/_/g, ' '); } function getRenderModeLabel(mode: string): string { // Automatically generate human-readable labels from render mode names return mode.charAt(0).toUpperCase() + mode.slice(1).replace(/_/g, ' '); } export function TopBar() { const settings = useStore($appSettings); const ui = useStore(uiState); const shader = useStore($shader); const input = useStore($input); const { setupAudio, disableAudio } = useAudio(); const [shareStatus, setShareStatus] = useState<'idle' | 'copied' | 'failed'>('idle'); const handleFullscreen = () => { if (!document.fullscreenElement) { document.documentElement.requestFullscreen(); } else { document.exitFullscreen(); } }; const handleRandom = () => { const randomCode = FakeShader.generateRandomCode(); setShaderCode(randomCode); }; const handleShare = () => { const shareData = { code: shader.code, resolution: settings.resolution, fps: settings.fps, renderMode: settings.renderMode, valueMode: settings.valueMode, uiOpacity: settings.uiOpacity, hueShift: settings.hueShift, }; try { const encoded = btoa(JSON.stringify(shareData)); const url = `${window.location.origin}${window.location.pathname}#${encoded}`; console.log('Sharing URL:', url); console.log('Share data:', shareData); navigator.clipboard .writeText(url) .then(() => { console.log('URL copied to clipboard'); setShareStatus('copied'); setTimeout(() => setShareStatus('idle'), 2000); }) .catch(() => { console.log('Copy failed'); setShareStatus('failed'); setTimeout(() => setShareStatus('idle'), 2000); }); } catch (error) { console.error('Failed to create share URL:', error); } }; const handleExportPNG = () => { const canvas = document.getElementById('canvas') as HTMLCanvasElement; if (canvas) { const link = document.createElement('a'); link.download = `bitfielder-${Date.now()}.png`; link.href = canvas.toDataURL('image/png'); link.click(); } }; const handleAudioToggle = async () => { if (input.audioEnabled) { disableAudio(); } else { await setupAudio(); } }; return (
Bitfielder
updateAppSettings({ hueShift: parseInt(e.target.value) }) } style={{ width: '80px', verticalAlign: 'middle', marginRight: '10px' }} /> updateAppSettings({ uiOpacity: parseInt(e.target.value) / 100 }) } style={{ width: '80px', verticalAlign: 'middle', marginRight: '10px' }} />
{shareStatus === 'copied' && (
Link copied to clipboard!
)} {shareStatus === 'failed' && (
Failed to copy link
)}
); }