diff --git a/packages/client/src/App.jsx b/packages/client/src/App.jsx index b37b446..af9caf1 100644 --- a/packages/client/src/App.jsx +++ b/packages/client/src/App.jsx @@ -15,7 +15,7 @@ import { usePinchZoom } from './hooks/usePinchZoom.js'; import { getModulesByCategory } from './engine/moduleRegistry.js'; import { useAuth } from './services/AuthContext.jsx'; -export default function App({ onSwitchToGame }) { +export default function App({ onSwitchToGame, onSwitchToWorkshop }) { const [, forceUpdate] = useState(0); const containerRef = useRef(null); const portPositions = useRef({}); @@ -281,6 +281,11 @@ export default function App({ onSwitchToGame }) { ๐ŸŽฎ Game )} + {onSwitchToWorkshop && !isMobile && ( + + )} Reaktor {!isMobile &&
} {!isMobile && ( diff --git a/packages/client/src/components/Workshop.jsx b/packages/client/src/components/Workshop.jsx new file mode 100644 index 0000000..3596ede --- /dev/null +++ b/packages/client/src/components/Workshop.jsx @@ -0,0 +1,228 @@ +import React, { useState, useEffect, useCallback } from 'react'; +import { workshop as workshopApi } from '../services/api.js'; +import { useAuth } from '../services/AuthContext.jsx'; +import { state, deserialize } from '../engine/state.js'; +import { serialize } from '../engine/state.js'; +import { rebuildGraph } from '../engine/audioEngine.js'; + +const TAGS = ['ambient', 'bass', 'drums', 'pad', 'lead', 'fx', 'chiptune', 'experimental']; + +function ShareModal({ onClose, onShared }) { + const [title, setTitle] = useState(''); + const [description, setDescription] = useState(''); + const [selectedTags, setSelectedTags] = useState([]); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(''); + + const handleShare = async () => { + if (!title.trim()) { setError('Titulo requerido'); return; } + if (state.modules.length === 0) { setError('No hay modulos en el canvas'); return; } + + setLoading(true); + setError(''); + try { + const patchData = serialize(); + await workshopApi.share({ + title: title.trim(), + description: description.trim(), + tags: selectedTags, + data: patchData, + }); + onShared?.(); + onClose(); + } catch (err) { + setError(err.message); + } + setLoading(false); + }; + + return ( +
+
e.stopPropagation()} style={{ gap: 14 }}> +

Compartir Patch

+ +
+ + setTitle(e.target.value)} /> + + +