Spaces:
Runtime error
Runtime error
File size: 2,688 Bytes
a45ece3 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | import { useState, useCallback, useMemo } from 'react'
import { Upload, Play } from 'lucide-react'
import { useAppStore } from '@/store/appStore'
import { formatBytes } from '@/lib/utils'
export function UploadPanel() {
const [file, setFile] = useState<File | null>(null)
const [duration, setDuration] = useState<number | null>(null)
const startRun = useAppStore((s) => s.startRun)
const onFiles = useCallback((files: FileList | null) => {
if (!files || files.length === 0) return
const f = files[0]
setFile(f)
// compute duration
const url = URL.createObjectURL(f)
const audio = new Audio(url)
audio.addEventListener('loadedmetadata', () => {
setDuration(audio.duration)
URL.revokeObjectURL(url)
})
}, [])
const handleDrop = useCallback((e: React.DragEvent<HTMLDivElement>) => {
e.preventDefault()
onFiles(e.dataTransfer.files)
}, [onFiles])
const handleBrowse = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
onFiles(e.target.files)
}, [onFiles])
const disabled = !file
const durationText = useMemo(() => {
if (duration == null) return '—'
const m = Math.floor(duration / 60)
const s = Math.round(duration % 60)
return `${m}:${s.toString().padStart(2, '0')}`
}, [duration])
return (
<div className="space-y-4" aria-label="Upload audio">
<div
onDragOver={(e) => e.preventDefault()}
onDrop={handleDrop}
className="border-2 border-dashed rounded-lg p-6 text-center hover:bg-muted/50"
>
<div className="flex flex-col items-center gap-2">
<Upload />
<p className="text-sm text-muted-foreground">Drag and drop WAV/MP3/M4A here or click to browse</p>
<input
type="file"
accept="audio/wav, audio/mpeg, audio/mp4, audio/x-m4a, audio/aac"
onChange={handleBrowse}
className="mt-2"
/>
</div>
</div>
{file && (
<div className="text-sm bg-secondary text-secondary-foreground rounded-md p-3 flex flex-wrap gap-4">
<span className="font-medium">{file.name}</span>
<span>Size: {formatBytes(file.size)}</span>
<span>Duration: {durationText}</span>
</div>
)}
<div className="flex justify-end">
<button
disabled={disabled}
onClick={() => file && startRun('audio', { file })}
className={`inline-flex items-center gap-2 px-4 py-2 rounded-md ${disabled ? 'bg-muted text-muted-foreground' : 'bg-primary text-primary-foreground hover:opacity-90'}`}
>
<Play size={16} /> Run
</button>
</div>
</div>
)
} |