Download react-generator.js from flen-crypto/algorithmic-harmony-maestro: direct link, hf CLI and curl.
- Browser
- Download file 11.5 kB
-
https://huggingface.co/spaces/flen-crypto/algorithmic-harmony-maestro/resolve/main/react-generator.js
- Command line
-
hf download hf://spaces/flen-crypto/algorithmic-harmony-maestro/react-generator.js
-
curl -L -o react-generator.js https://huggingface.co/spaces/flen-crypto/algorithmic-harmony-maestro/resolve/main/react-generator.js
11.5 kB
| import React, { useMemo, useState } from "react"; | |
| import { motion } from "framer-motion"; | |
| import { Play, Pause, UploadCloud, Sparkles, Music, Settings2, Gauge, Share2, Wand2 } from "lucide-react"; | |
| import { ResponsiveContainer, RadialBarChart, RadialBar, PolarAngleAxis } from "recharts"; | |
| import { | |
| Card, | |
| CardContent, | |
| CardDescription, | |
| CardFooter, | |
| CardHeader, | |
| CardTitle, | |
| } from "@/components/ui/card"; | |
| import { Button } from "@/components/ui/button"; | |
| import { Input } from "@/components/ui/input"; | |
| import { Textarea } from "@/components/ui/textarea"; | |
| import { Label } from "@/components/ui/label"; | |
| import { Slider } from "@/components/ui/slider"; | |
| import { Switch } from "@/components/ui/switch"; | |
| import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; | |
| import { Separator } from "@/components/ui/separator"; | |
| // --- Helper comps --- | |
| const Stat = ({ label, value, hint }: { label: string; value: string; hint?: string }) => ( | |
| <div className="flex flex-col"> | |
| <span className="text-xs text-muted-foreground">{label}</span> | |
| <span className="font-semibold">{value}</span> | |
| {hint ? <span className="text-[11px] text-muted-foreground">{hint}</span> : null} | |
| </div> | |
| ); | |
| const ScoreGauge = ({ score = 72, label = "Algorithm Fit" }: { score?: number; label?: string }) => { | |
| const data = useMemo(() => [{ name: label, value: score, fill: "hsl(var(--primary))" }], [score, label]); | |
| return ( | |
| <div className="h-40 w-40"> | |
| <ResponsiveContainer> | |
| <RadialBarChart innerRadius="70%" outerRadius="100%" data={data} startAngle={220} endAngle={-40}> | |
| <PolarAngleAxis type="number" domain={[0, 100]} tick={false} /> | |
| <RadialBar dataKey="value" cornerRadius={20} /> | |
| </RadialBarChart> | |
| </ResponsiveContainer> | |
| <div className="-mt-24 text-center"> | |
| <div className="text-3xl font-bold">{score}</div> | |
| <div className="text-xs text-muted-foreground">{label}</div> | |
| </div> | |
| </div> | |
| ); | |
| }; | |
| export default function PromptComposer() { | |
| const [prompt, setPrompt] = useState("Upbeat 115 BPM house track with soulful vocals; short 5s intro; bright mix; optimized for workout playlists."); | |
| const [title, setTitle] = useState("Moving On (Pop‑Punk Revival)"); | |
| const [tempo, setTempo] = useState(115); | |
| const [lufs, setLufs] = useState(-14); | |
| const [autoMaster, setAutoMaster] = useState(true); | |
| const [generating, setGenerating] = useState(false); | |
| const [playing, setPlaying] = useState(false); | |
| const score = useMemo(() => { | |
| // Fake score from inputs (for mock only) | |
| let s = 60 + Math.min(20, Math.max(0, 120 - Math.abs(120 - tempo)) / 2); | |
| s += autoMaster ? 8 : 0; | |
| s += lufs >= -14 && lufs <= -13 ? 6 : 0; | |
| return Math.min(99, Math.round(s)); | |
| }, [tempo, lufs, autoMaster]); | |
| const recs = useMemo(() => { | |
| const list: string[] = []; | |
| if (prompt.toLowerCase().includes("intro") === false) list.push("Add a clear hook within first 10s to reduce early skips."); | |
| if (lufs < -16) list.push("Raise loudness toward −14 LUFS (Spotify norm)." ); | |
| if (tempo < 100) list.push("Consider +10–15 BPM for workout playlists."); | |
| list.push("Tag with 3–5 micro‑genres your audience actually searches."); | |
| list.push("Add call‑to‑action in description (save/share)." ); | |
| return list; | |
| }, [prompt, lufs, tempo]); | |
| const handleGenerate = async () => { | |
| setGenerating(true); | |
| await new Promise(r => setTimeout(r, 1200)); // simulate | |
| setGenerating(false); | |
| }; | |
| return ( | |
| <div className="mx-auto max-w-6xl p-6 space-y-6"> | |
| <div className="flex items-center gap-3"> | |
| <motion.div initial={{ opacity: 0, y: -6 }} animate={{ opacity: 1, y: 0 }}> | |
| <Sparkles className="h-6 w-6 text-primary" /> | |
| </motion.div> | |
| <h1 className="text-2xl md:text-3xl font-semibold">Prompt Composer — AI‑Aware Music Creation</h1> | |
| </div> | |
| <Card className="shadow-sm"> | |
| <CardHeader> | |
| <CardTitle className="flex items-center gap-2"><Wand2 className="h-5 w-5"/> Compose from Prompt</CardTitle> | |
| <CardDescription>Describe your track. We’ll structure it for generation and platform algorithms.</CardDescription> | |
| </CardHeader> | |
| <CardContent className="space-y-4"> | |
| <div className="grid md:grid-cols-2 gap-4"> | |
| <div className="space-y-3"> | |
| <Label htmlFor="prompt">Music prompt</Label> | |
| <Textarea id="prompt" value={prompt} onChange={e => setPrompt(e.target.value)} className="min-h-[110px]" /> | |
| <div className="grid grid-cols-3 gap-3"> | |
| <div> | |
| <Label>Tempo (BPM)</Label> | |
| <div className="flex items-center gap-3"> | |
| <Slider value={[tempo]} min={70} max={200} step={1} onValueChange={(v) => setTempo(v[0])} /> | |
| <div className="w-10 text-right text-sm">{tempo}</div> | |
| </div> | |
| </div> | |
| <div> | |
| <Label>Target loudness (LUFS)</Label> | |
| <div className="flex items-center gap-3"> | |
| <Slider value={[lufs]} min={-20} max={-10} step={0.5} onValueChange={(v) => setLufs(v[0])} /> | |
| <div className="w-12 text-right text-sm">{lufs}</div> | |
| </div> | |
| </div> | |
| <div className="flex items-center gap-3 pt-6"> | |
| <Switch checked={autoMaster} onCheckedChange={setAutoMaster} id="am" /> | |
| <Label htmlFor="am">Auto‑master</Label> | |
| </div> | |
| </div> | |
| </div> | |
| <div className="space-y-3"> | |
| <Label>Working title</Label> | |
| <Input value={title} onChange={e => setTitle(e.target.value)} /> | |
| <div className="grid grid-cols-2 gap-3"> | |
| <Stat label="Hook ETA" value="0:08" hint="Aim < 0:15" /> | |
| <Stat label="Energy curve" value="Rising → Chorus" /> | |
| <Stat label="Key (est.)" value="A minor" /> | |
| <Stat label="Mood" value="Upbeat • Confident" /> | |
| </div> | |
| </div> | |
| </div> | |
| </CardContent> | |
| <CardFooter className="flex items-center justify-between"> | |
| <Button size="sm" variant="secondary" className="gap-2" onClick={() => setPrompt(p => p + (p.endsWith(".") ? "" : ".") + " Short 8s intro; keep vocals upfront; chorus by 0:20.")}>Quick improve</Button> | |
| <Button className="gap-2" onClick={handleGenerate} disabled={generating}> | |
| <Sparkles className="h-4 w-4"/> | |
| {generating ? "Generating…" : "Generate mock"} | |
| </Button> | |
| </CardFooter> | |
| </Card> | |
| <div className="grid md:grid-cols-3 gap-4"> | |
| <Card> | |
| <CardHeader className="pb-2"><CardTitle className="flex items-center gap-2"><Gauge className="h-5 w-5"/> Algorithm Fit</CardTitle></CardHeader> | |
| <CardContent className="flex items-center justify-center"> | |
| <ScoreGauge score={score} /> | |
| </CardContent> | |
| <CardFooter className="text-xs text-muted-foreground">Based on tempo proximity, mastering target and intro/hook heuristics.</CardFooter> | |
| </Card> | |
| <Card> | |
| <CardHeader className="pb-2"><CardTitle className="flex items-center gap-2"><Settings2 className="h-5 w-5"/> Metadata Optimizer</CardTitle></CardHeader> | |
| <CardContent className="space-y-3 text-sm"> | |
| <div> | |
| <Label>SoundCloud</Label> | |
| <ul className="list-disc pl-5 mt-1 space-y-1"> | |
| <li>Genre: <b>house</b></li> | |
| <li>Tags: soulful, workout, vocal house, 115bpm</li> | |
| <li>Desc: “Hook by 0:08 • Bright mix • Save & share if it moves you.”</li> | |
| </ul> | |
| </div> | |
| <Separator /> | |
| <div> | |
| <Label>Spotify</Label> | |
| <ul className="list-disc pl-5 mt-1 space-y-1"> | |
| <li>Playlists fit: <b>Cardio</b>, <b>Dance Rising</b>, <b>mint Fresh</b></li> | |
| <li>Keywords: upbeat, energetic, vocal, gym</li> | |
| <li>Loudness target: −14 LUFS (true‑peak ≤ −1 dB)</li> | |
| </ul> | |
| </div> | |
| </CardContent> | |
| </Card> | |
| <Card> | |
| <CardHeader className="pb-2"><CardTitle className="flex items-center gap-2"><Music className="h-5 w-5"/> Prescriptive Advice</CardTitle></CardHeader> | |
| <CardContent> | |
| <ul className="list-disc pl-5 text-sm space-y-1"> | |
| {recs.map((r, i) => ( | |
| <li key={i}>{r}</li> | |
| ))} | |
| </ul> | |
| </CardContent> | |
| </Card> | |
| </div> | |
| <Card> | |
| <CardHeader> | |
| <CardTitle className="flex items-center gap-2"><Share2 className="h-5 w-5"/> Export & Upload (Mock)</CardTitle> | |
| <CardDescription>Preview how you'd ship this to platforms. Buttons are disabled in the mock.</CardDescription> | |
| </CardHeader> | |
| <CardContent className="grid md:grid-cols-3 gap-4"> | |
| <Card className="border-dashed"> | |
| <CardHeader> | |
| <CardTitle className="text-base">Audio</CardTitle> | |
| <CardDescription>2:57 • 115 BPM • A min</CardDescription> | |
| </CardHeader> | |
| <CardContent> | |
| <div className="flex items-center gap-3"> | |
| <Button variant="secondary" size="icon" className="rounded-full" onClick={() => setPlaying(p=>!p)}> | |
| {playing ? <Pause className="h-4 w-4"/> : <Play className="h-4 w-4"/>} | |
| </Button> | |
| <div className="text-sm text-muted-foreground">(Mock preview)</div> | |
| </div> | |
| </CardContent> | |
| <CardFooter> | |
| <Button size="sm" variant="outline" disabled className="gap-2"><UploadCloud className="h-4 w-4"/> Upload WAV</Button> | |
| </CardFooter> | |
| </Card> | |
| <Card className="border-dashed"> | |
| <CardHeader> | |
| <CardTitle className="text-base">SoundCloud Package</CardTitle> | |
| </CardHeader> | |
| <CardContent className="text-sm space-y-2"> | |
| <div>Title: <b>{title}</b></div> | |
| <div>Genre: <b>house</b></div> | |
| <div>Tags: soulful; workout; vocal‑house; 115bpm</div> | |
| </CardContent> | |
| <CardFooter> | |
| <Button size="sm" variant="outline" disabled className="gap-2"><UploadCloud className="h-4 w-4"/> Upload to SC</Button> | |
| </CardFooter> | |
| </Card> | |
| <Card className="border-dashed"> | |
| <CardHeader> | |
| <CardTitle className="text-base">Spotify Package</CardTitle> | |
| </CardHeader> | |
| <CardContent className="text-sm space-y-2"> | |
| <div>Title: <b>{title}</b></div> | |
| <div>Playlist pitch: cardio • dance rising • gym anthems</div> | |
| <div>Master target: −14 LUFS / −1 dBTP</div> | |
| </CardContent> | |
| <CardFooter> | |
| <Button size="sm" variant="outline" disabled className="gap-2"><UploadCloud className="h-4 w-4"/> Upload to Spotify</Button> | |
| </CardFooter> | |
| </Card> | |
| </CardContent> | |
| </Card> | |
| <div className="text-xs text-muted-foreground text-center"> | |
| SIMULATED — No external calls. Replace with Suno/Udio + platform SDKs in a real build. | |
| </div> | |
| </div> | |
| ); | |
| } |