Mina Emadi
implemented some UI changes including adding a knob for pan and reverb, changing the UI of the key and bpm modification and changing the appearance of the detected key and bpm and removed some dev
e631a15
import React, { useState, useCallback, useEffect } from 'react'
/* Upload slots — hidden
const STEM_TYPES = [
{ name: 'guitar', label: 'Guitar', icon: '🎸' },
{ name: 'drums', label: 'Drums', icon: '🥁' },
{ name: 'bass', label: 'Bass', icon: '🎸' },
{ name: 'synth', label: 'Synth', icon: '🎹' },
{ name: 'click_record', label: 'Click Record', icon: '⏱️' }
]
*/
function FileUpload({ onUpload, onLoadPreset, loading, error, onClearError, cacheStatus = {} }) {
const [presets, setPresets] = useState([])
const [selectedPreset, setSelectedPreset] = useState('')
useEffect(() => {
fetch('/api/presets')
.then(r => r.json())
.then(data => setPresets(data.presets || []))
.catch(() => {})
}, [])
/* Upload slots — hidden
const [stems, setStems] = useState({
guitar: null,
drums: null,
bass: null,
synth: null,
click_record: null
})
const [midiFiles, setMidiFiles] = useState([])
const [midiDragActive, setMidiDragActive] = useState(false)
const handleStemSelect = useCallback((stemType, file) => {
setStems(prev => ({ ...prev, [stemType]: file }))
}, [])
const handleStemRemove = useCallback((stemType) => {
setStems(prev => ({ ...prev, [stemType]: null }))
}, [])
const handleMidiSelect = useCallback((e) => {
const selectedFiles = Array.from(e.target.files).filter(f =>
f.name.toLowerCase().endsWith('.mid') || f.name.toLowerCase().endsWith('.midi')
)
setMidiFiles(prev => [...prev, ...selectedFiles])
}, [])
const handleMidiRemove = useCallback((index) => {
setMidiFiles(prev => prev.filter((_, i) => i !== index))
}, [])
const handleMidiDrag = useCallback((e) => {
e.preventDefault()
e.stopPropagation()
if (e.type === 'dragenter' || e.type === 'dragover') {
setMidiDragActive(true)
} else if (e.type === 'dragleave') {
setMidiDragActive(false)
}
}, [])
const handleMidiDrop = useCallback((e) => {
e.preventDefault()
e.stopPropagation()
setMidiDragActive(false)
const droppedFiles = Array.from(e.dataTransfer.files).filter(f =>
f.name.toLowerCase().endsWith('.mid') || f.name.toLowerCase().endsWith('.midi')
)
setMidiFiles(prev => [...prev, ...droppedFiles])
}, [])
const handleSubmit = useCallback(async () => {
const formData = new FormData()
Object.entries(stems).forEach(([name, file]) => {
if (file) formData.append(name, file)
})
midiFiles.forEach(file => {
formData.append('midi', file)
})
await onUpload(formData)
}, [stems, midiFiles, onUpload])
const stemCount = Object.values(stems).filter(f => f !== null).length
const hasFiles = stemCount > 0
*/
return (
<div className="glass rounded-2xl p-6 animate-fade-in max-w-4xl mx-auto">
<h2 className="text-xl font-semibold mb-6 text-center">Load a Demo Track</h2>
{/* Demo presets dropdown */}
{presets.length > 0 && (
<div className="p-4 bg-gray-800/50 rounded-xl border border-gray-600">
<div className="flex gap-3">
<select
value={selectedPreset}
onChange={(e) => setSelectedPreset(e.target.value)}
disabled={loading}
className="flex-1 bg-gray-800 border border-gray-600 rounded-lg px-3 py-2 text-white text-sm focus:outline-none focus:border-primary-500"
>
<option value="">Select a demo...</option>
{presets.map(p => {
const status = cacheStatus[p]
const label = status === 'cached'
? `${p} ✓`
: status === 'loading'
? `${p} …`
: p
return <option key={p} value={p}>{label}</option>
})}
</select>
<button
onClick={() => selectedPreset && onLoadPreset(selectedPreset)}
disabled={loading || !selectedPreset}
className="px-4 py-2 rounded-lg text-sm font-medium transition-all disabled:bg-gray-700 disabled:text-gray-500 disabled:cursor-not-allowed bg-gradient-to-r from-primary-600 to-accent-600 hover:from-primary-500 hover:to-accent-500"
>
{loading ? 'Loading...' : 'Load'}
</button>
</div>
</div>
)}
{/* Upload slots — hidden
<div className="grid grid-cols-2 gap-4 mb-6">
{STEM_TYPES.map(({ name, label, icon }) => (
<StemUploadCard
key={name}
name={name}
label={label}
icon={icon}
file={stems[name]}
onSelect={(file) => handleStemSelect(name, file)}
onRemove={() => handleStemRemove(name)}
disabled={loading}
/>
))}
</div>
<div
onDragEnter={handleMidiDrag}
onDragLeave={handleMidiDrag}
onDragOver={handleMidiDrag}
onDrop={handleMidiDrop}
className={`
mt-6 p-4 border-2 border-dashed rounded-xl transition-all
${midiDragActive
? 'border-blue-500 bg-blue-500/10'
: 'border-gray-600'
}
`}
>
<div className="flex items-center justify-between mb-3">
<div className="flex items-center gap-2">
<span className="text-2xl">🎹</span>
<span className="font-medium text-gray-300">MIDI Files (Optional)</span>
</div>
<label
htmlFor="midi-input"
className="px-4 py-2 bg-gray-700 hover:bg-gray-600 rounded-lg cursor-pointer transition-colors text-sm"
>
+ Add MIDI
</label>
<input
type="file"
multiple
accept=".mid,.midi"
onChange={handleMidiSelect}
className="hidden"
id="midi-input"
disabled={loading}
/>
</div>
{midiFiles.length > 0 && (
<div className="space-y-2">
{midiFiles.map((file, index) => (
<div
key={index}
className="flex items-center justify-between bg-gray-800/50 rounded-lg px-3 py-2"
>
<div className="flex items-center gap-2">
<span className="text-sm truncate">{file.name}</span>
<span className="text-xs text-gray-500">
{(file.size / 1024).toFixed(1)} KB
</span>
</div>
<button
onClick={() => handleMidiRemove(index)}
className="text-gray-400 hover:text-red-400 transition-colors"
disabled={loading}
>
</button>
</div>
))}
</div>
)}
</div>
<button
onClick={handleSubmit}
disabled={loading || !hasFiles}
className={`
w-full mt-6 py-3 rounded-xl font-medium transition-all
${loading || !hasFiles
? 'bg-gray-600 cursor-not-allowed'
: 'bg-gradient-to-r from-primary-600 to-accent-600 hover:from-primary-500 hover:to-accent-500'
}
`}
>
{loading ? (
<span className="flex items-center justify-center gap-2">
<span className="animate-spin"></span>
Analyzing...
</span>
) : (
`Upload ${stemCount} Stem${stemCount !== 1 ? 's' : ''}${midiFiles.length > 0 ? ` + ${midiFiles.length} MIDI` : ''}`
)}
</button>
*/}
{/* Error display */}
{error && (
<div className="mt-4 p-3 bg-red-500/20 border border-red-500/50 rounded-lg text-red-300 text-sm flex items-center justify-between">
<span>{error}</span>
<button
onClick={onClearError}
className="text-red-400 hover:text-red-200"
>
</button>
</div>
)}
</div>
)
}
/* StemUploadCard — hidden
function StemUploadCard({ name, label, icon, file, onSelect, onRemove, disabled }) {
const [dragActive, setDragActive] = React.useState(false)
const handleFileChange = (e) => {
const selectedFile = e.target.files[0]
if (selectedFile && selectedFile.name.toLowerCase().endsWith('.wav')) {
onSelect(selectedFile)
}
}
const handleDrag = (e) => {
e.preventDefault()
e.stopPropagation()
if (e.type === 'dragenter' || e.type === 'dragover') {
setDragActive(true)
} else if (e.type === 'dragleave') {
setDragActive(false)
}
}
const handleDrop = (e) => {
e.preventDefault()
e.stopPropagation()
setDragActive(false)
const droppedFile = e.dataTransfer.files[0]
if (droppedFile && droppedFile.name.toLowerCase().endsWith('.wav')) {
onSelect(droppedFile)
}
}
return (
<div
onDragEnter={handleDrag}
onDragLeave={handleDrag}
onDragOver={handleDrag}
onDrop={handleDrop}
className={`
p-4 rounded-xl border-2 transition-all
${file
? 'border-green-500 bg-green-500/10'
: dragActive
? 'border-blue-500 bg-blue-500/10'
: 'border-gray-600 hover:border-primary-500 bg-gray-800/30'
}
`}
>
<div className="flex items-center justify-between mb-3">
<div className="flex items-center gap-2">
<span className="text-2xl">{icon}</span>
<span className="font-medium text-gray-300">{label}</span>
</div>
{file && (
<button
onClick={onRemove}
className="text-gray-400 hover:text-red-400 transition-colors"
disabled={disabled}
>
</button>
)}
</div>
{file ? (
<div className="text-sm">
<p className="text-gray-300 truncate">{file.name}</p>
<p className="text-gray-500 text-xs mt-1">
{(file.size / 1024 / 1024).toFixed(1)} MB
</p>
</div>
) : (
<label
htmlFor={`${name}-input`}
className="block w-full py-2 text-center bg-gray-700 hover:bg-gray-600 rounded-lg cursor-pointer transition-colors text-sm"
>
Select File
</label>
)}
<input
type="file"
accept=".wav"
onChange={handleFileChange}
className="hidden"
id={`${name}-input`}
disabled={disabled}
/>
</div>
)
}
*/
export default FileUpload