Build standalone MelodyMuse SPA
Drop the Supabase backend and call the AI provider directly from the
browser. The endpoint URL, API key, and model name are stored in
localStorage and used for direct /chat/completions requests.
- src/lib/llm.ts: config persistence, direct fetch, JSON extraction,
shape validation, connection test
- src/lib/prompts.ts: full + partial-regeneration system prompts and
user-message builder
- src/lib/{api,supabase}.ts removed
- supabase/ directory removed
- @supabase/supabase-js dropped from package.json
- README updated to describe the standalone architecture and CORS caveats
- .gitignore: drop Supabase entries, exclude *.tsbuildinfo
This commit is contained in:
@@ -1,410 +1,121 @@
|
||||
\# 🎵 MelodyMuse
|
||||
# MelodyMuse
|
||||
|
||||
Generate all assets needed for a Suno AI song from a free-text description —
|
||||
titles, lyrics, style prompt, video prompts (Abstract / Cinematic / Hybrid),
|
||||
and a YouTube description, then download everything as a ZIP.
|
||||
|
||||
The app runs **entirely in your browser** as a single static SPA. There is no
|
||||
backend, no database, no third-party services: the only network calls are
|
||||
direct `fetch` requests from your browser to the AI provider's
|
||||
OpenAI-compatible `/chat/completions` endpoint, using credentials that you
|
||||
paste into the Settings page. Those credentials are kept in this browser's
|
||||
`localStorage`.
|
||||
|
||||
> AI-powered song asset generator for \[Suno AI](https://suno.com) — lyrics, style prompts, video loops, and YouTube descriptions in one click.
|
||||
|
||||
|
||||
|
||||
\---
|
||||
|
||||
|
||||
|
||||
\## What is MelodyMuse?
|
||||
|
||||
|
||||
|
||||
MelodyMuse is a personal web app that takes a music idea as input and uses an AI (via any OpenAI-compatible API) to generate everything you need to publish a Suno AI song:
|
||||
|
||||
|
||||
|
||||
\- ✍️ \*\*Lyrics\*\* — rhyming, structured with Suno section tags, free of genre references
|
||||
|
||||
\- 🎵 \*\*Style Prompt\*\* — ready to paste into Suno's "Style of Music" field
|
||||
|
||||
\- 🚫 \*\*Negative Style Prompt\*\* — what Suno should avoid
|
||||
|
||||
\- 🏷️ \*\*3 Song Title Suggestions\*\* — atmospheric, direct, and abstract
|
||||
|
||||
\- 🎬 \*\*3 Video Loop Prompts\*\* — abstract, cinematic, and hybrid, with AI tool recommendations
|
||||
|
||||
\- 📺 \*\*YouTube Description\*\* — with embedded lyrics, hashtags, and a call to action
|
||||
|
||||
|
||||
|
||||
All results are editable before download. Everything packages into a single `.zip` file named after your chosen song title.
|
||||
|
||||
|
||||
|
||||
\---
|
||||
|
||||
|
||||
|
||||
\## Features
|
||||
|
||||
|
||||
|
||||
| Feature | Details |
|
||||
|
||||
|---|---|
|
||||
|
||||
| Free-text input | Describe your music idea in natural language |
|
||||
|
||||
| Optional parameters | Language, mood, vocals vs. instrumental |
|
||||
|
||||
| Inline editing | Edit any generated section before downloading |
|
||||
|
||||
| Per-section regeneration | Regenerate only lyrics, style, titles, etc. |
|
||||
|
||||
| Full regeneration | Re-run everything with one click |
|
||||
|
||||
| ZIP download | `Style.txt`, `Text.txt`, `Videodescription.txt`, `Videoprompt.txt` |
|
||||
|
||||
| Dark / Light mode | Toggle in the top-right corner |
|
||||
|
||||
| Secure API key storage | Key lives on the server only — never in the browser |
|
||||
|
||||
| Provider-agnostic | Works with any OpenAI-compatible API (MiniMax, OpenAI, Mistral, etc.) |
|
||||
|
||||
|
||||
|
||||
\---
|
||||
|
||||
|
||||
|
||||
\## Tech Stack
|
||||
|
||||
|
||||
|
||||
\- \*\*Frontend:\*\* React 18 + TypeScript + Tailwind CSS
|
||||
|
||||
\- \*\*Backend:\*\* Supabase Edge Functions (Deno)
|
||||
|
||||
\- \*\*Database:\*\* Supabase PostgreSQL (app config only)
|
||||
|
||||
\- \*\*ZIP generation:\*\* JSZip (client-side)
|
||||
|
||||
|
||||
|
||||
\---
|
||||
|
||||
|
||||
|
||||
\## Project Structure
|
||||
## Tech stack
|
||||
|
||||
- React 18 + TypeScript + Vite
|
||||
- Tailwind CSS (dark by default, light theme via `html.light`)
|
||||
- JSZip for client-side ZIP generation
|
||||
- `react-router-dom` for the two routes (`/`, `/settings`)
|
||||
|
||||
## Repository layout
|
||||
|
||||
```
|
||||
|
||||
/
|
||||
|
||||
MelodyMuse/
|
||||
├── src/
|
||||
|
||||
│ ├── pages/
|
||||
|
||||
│ │ ├── Index.tsx # Main generator page
|
||||
|
||||
│ │ └── Settings.tsx # API configuration page
|
||||
|
||||
│ ├── components/
|
||||
|
||||
│ │ ├── InputPanel.tsx # Left panel: text input + options
|
||||
|
||||
│ │ ├── ResultsPanel.tsx # Right panel: all result cards
|
||||
|
||||
│ │ ├── TitlesCard.tsx # Song title chips + selection
|
||||
|
||||
│ │ ├── LyricsCard.tsx # Editable lyrics textarea
|
||||
|
||||
│ │ ├── StyleCard.tsx # Style + negative style
|
||||
|
||||
│ │ ├── VideoPromptsCard.tsx # Tabbed video prompt variations
|
||||
|
||||
│ │ ├── YoutubeCard.tsx # YouTube description
|
||||
|
||||
│ │ └── DownloadBar.tsx # Sticky ZIP download bar
|
||||
|
||||
│ └── lib/
|
||||
|
||||
│ └── zip.ts # ZIP file generation logic
|
||||
|
||||
└── supabase/
|
||||
|
||||
  └── functions/
|
||||
|
||||
  ├── save-config/ # Saves API credentials server-side
|
||||
|
||||
  ├── get-config-display/ # Returns masked config for settings UI
|
||||
|
||||
  ├── test-connection/ # Tests the configured API
|
||||
|
||||
  └── generate-song/ # Calls AI and returns generated assets
|
||||
|
||||
│ ├── App.tsx
|
||||
│ ├── main.tsx
|
||||
│ ├── index.css # Tailwind layers + design-system components
|
||||
│ ├── components/ # UI building blocks
|
||||
│ │ └── cards/ # The five result cards
|
||||
│ ├── pages/ # HomePage, SettingsPage
|
||||
│ ├── lib/
|
||||
│ │ ├── llm.ts # direct fetch → provider, JSON extraction, config persistence
|
||||
│ │ ├── prompts.ts # system + user prompt construction
|
||||
│ │ ├── types.ts # shared TypeScript types
|
||||
│ │ ├── zip.ts # JSZip layout
|
||||
│ │ ├── theme.ts # dark/light mode
|
||||
│ │ └── toast.tsx # toast context
|
||||
│ └── vite-env.d.ts
|
||||
├── public/favicon.svg
|
||||
├── index.html
|
||||
├── tailwind.config.js
|
||||
├── vite.config.ts
|
||||
├── tsconfig*.json
|
||||
└── package.json
|
||||
```
|
||||
|
||||
## Getting started
|
||||
|
||||
1. **Install dependencies**
|
||||
|
||||
\---
|
||||
```sh
|
||||
npm install
|
||||
```
|
||||
|
||||
2. **Start the dev server**
|
||||
|
||||
```sh
|
||||
npm run dev
|
||||
```
|
||||
|
||||
\## Setup
|
||||
Open <http://localhost:5173> in your browser.
|
||||
|
||||
3. **Open Settings** and fill in:
|
||||
|
||||
- **API Endpoint URL** — e.g. `https://api.minimax.chat/v1`
|
||||
- **API Key** — your provider's secret key
|
||||
- **Model Name** — e.g. `MiniMax-M3`
|
||||
|
||||
\### 1. Prerequisites
|
||||
Click **Save Configuration**, then **Test Connection** to confirm
|
||||
everything is wired up.
|
||||
|
||||
4. **Generate**. Return to the home page, type a music idea, click
|
||||
**Generate Song Assets**.
|
||||
|
||||
## Build for production
|
||||
|
||||
\- A \[Supabase](https://supabase.com) project (free tier is sufficient)
|
||||
|
||||
\- An API key from any OpenAI-compatible provider (e.g. MiniMax, OpenAI, Mistral)
|
||||
|
||||
|
||||
|
||||
\---
|
||||
|
||||
|
||||
|
||||
\### 2. Supabase — Create the config table
|
||||
|
||||
|
||||
|
||||
In your Supabase project, open the \*\*SQL Editor\*\* and run:
|
||||
|
||||
|
||||
|
||||
```sql
|
||||
|
||||
CREATE TABLE app\_settings (
|
||||
|
||||
  id INT PRIMARY KEY DEFAULT 1,
|
||||
|
||||
  api\_endpoint TEXT NOT NULL DEFAULT '',
|
||||
|
||||
  api\_key TEXT NOT NULL DEFAULT '',
|
||||
|
||||
  model\_name TEXT NOT NULL DEFAULT '',
|
||||
|
||||
  updated\_at TIMESTAMPTZ DEFAULT NOW()
|
||||
|
||||
);
|
||||
|
||||
|
||||
|
||||
INSERT INTO app\_settings (id, api\_endpoint, api\_key, model\_name)
|
||||
|
||||
VALUES (1, '', '', '')
|
||||
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
```sh
|
||||
npm run build
|
||||
```
|
||||
|
||||
Outputs static assets in `dist/`. The `dist/` folder is a normal SPA — serve
|
||||
it from any static host (GitHub Pages, Netlify, Vercel, `python -m http.server`,
|
||||
…).
|
||||
|
||||
> ⚠️ **Heads up about deployment.** Because the API key is stored in the
|
||||
> browser's `localStorage`, you should not serve a public deployment of this
|
||||
> app and use it with a real key on a shared device. For personal/local use
|
||||
> this is fine.
|
||||
|
||||
> \*\*Important:\*\* Row Level Security (RLS) must be \*\*disabled\*\* for this table.
|
||||
## CORS
|
||||
|
||||
> The table is only ever accessed by Edge Functions using the service\_role key —
|
||||
The app makes direct cross-origin requests from the browser to your
|
||||
provider. If your provider does not send the right `Access-Control-Allow-*`
|
||||
headers for your origin, the request will fail with a CORS error. The
|
||||
generated error message will explicitly call this out. Workarounds:
|
||||
|
||||
> never directly from the browser.
|
||||
- Pick a provider/endpoint that already permits browser CORS (most managed
|
||||
OpenAI-compatible services do).
|
||||
- Run the app on the same origin as the API (i.e. front it with a tiny
|
||||
proxy).
|
||||
- Use a CORS-permissive browser extension during local development.
|
||||
|
||||
## Provider format
|
||||
|
||||
The configured endpoint must expose an OpenAI-compatible
|
||||
`POST {api_endpoint}/chat/completions` route that accepts
|
||||
`{ model, messages, max_tokens }` and returns
|
||||
`{ choices: [{ message: { content } }] }`.
|
||||
|
||||
\---
|
||||
|
||||
|
||||
|
||||
\### 3. Configure your API key in the app
|
||||
|
||||
|
||||
|
||||
1\. Open the deployed app and navigate to \*\*Settings\*\* (link below the Generate button)
|
||||
|
||||
2\. Enter your:
|
||||
|
||||
  - \*\*API Endpoint URL\*\* — e.g. `https://api.minimax.chat/v1`
|
||||
|
||||
  - \*\*API Key\*\* — your provider's secret key
|
||||
|
||||
  - \*\*Model Name\*\* — e.g. `MiniMax-Text-01` or `gpt-4o`
|
||||
|
||||
3\. Click \*\*Save Configuration\*\*
|
||||
|
||||
4\. Use \*\*Test Connection\*\* to verify everything works
|
||||
|
||||
|
||||
|
||||
Your API key is sent to the server via an Edge Function and stored in the database.
|
||||
|
||||
It is \*\*never\*\* returned to the browser or stored in localStorage.
|
||||
|
||||
|
||||
|
||||
\---
|
||||
|
||||
|
||||
|
||||
\## Usage
|
||||
|
||||
|
||||
|
||||
1\. Open the app at your URL
|
||||
|
||||
2\. Enter a music idea in the text field
|
||||
|
||||
  > \_Example: "Melancholic lo-fi house beat for a rainy Sunday morning"\_
|
||||
|
||||
3\. Optionally expand \*\*Options\*\* to set language, mood, and vocal preference
|
||||
|
||||
4\. Click \*\*✦ Generate Song Assets\*\*
|
||||
|
||||
5\. Review and edit any section inline
|
||||
|
||||
6\. Select your preferred song title from the 3 suggestions
|
||||
|
||||
7\. Click \*\*⬇ Download ZIP\*\* — you'll get `YourTitle.zip` containing:
|
||||
|
||||
|
||||
|
||||
| File | Contents |
|
||||
|
||||
|---|---|
|
||||
|
||||
| `Style.txt` | Suno style prompt + negative style (always English) |
|
||||
|
||||
| `Text.txt` | Song lyrics with Suno section tags |
|
||||
|
||||
| `Videodescription.txt` | Full YouTube description (in lyrics language) |
|
||||
|
||||
| `Videoprompt.txt` | 3 video loop prompts with tool recommendations |
|
||||
|
||||
|
||||
|
||||
\---
|
||||
|
||||
|
||||
|
||||
\## AI Output Rules
|
||||
|
||||
|
||||
|
||||
The AI follows these rules automatically — no manual configuration needed:
|
||||
|
||||
|
||||
|
||||
\*\*Lyrics\*\*
|
||||
|
||||
\- Must rhyme (AABB or ABAB scheme)
|
||||
|
||||
\- Must NOT reference the genre, instruments, or music production
|
||||
|
||||
  \_(e.g. a classical piano piece will not have lyrics about pianos or orchestras)\_
|
||||
|
||||
\- Structured with Suno section tags: `\[Verse]`, `\[Chorus]`, `\[Bridge]`, `\[Outro]`, etc.
|
||||
|
||||
|
||||
|
||||
\*\*Style Prompt\*\*
|
||||
|
||||
\- Max 120 words, comma-separated
|
||||
|
||||
\- Includes: tempo (BPM), instruments, production style, texture, energy, mood, vocal style
|
||||
|
||||
\- Never references artists or bands by name
|
||||
|
||||
|
||||
|
||||
\*\*Video Prompts\*\*
|
||||
|
||||
\- 3 variations: Abstract, Cinematic, Hybrid
|
||||
|
||||
\- Optimized for 5-second seamless loops at 16:9
|
||||
|
||||
\- Looping logic: if reverse playback is used, only physically sensible elements
|
||||
|
||||
  \_(e.g. pulsing light — never reversed rainfall)\_
|
||||
|
||||
\- No text, logos, faces, or hands in any prompt
|
||||
|
||||
\- Includes tool recommendation (Runway, Kling, Luma, Pika, Haiper — never Sora)
|
||||
|
||||
|
||||
|
||||
\*\*Language\*\*
|
||||
|
||||
\- Style, negative style, and video prompts → always \*\*English\*\*
|
||||
|
||||
\- Lyrics, titles, YouTube description → in the \*\*user's selected language\*\*
|
||||
|
||||
|
||||
|
||||
\---
|
||||
|
||||
|
||||
|
||||
\## Supported API Providers
|
||||
|
||||
|
||||
|
||||
MelodyMuse works with any OpenAI-compatible API:
|
||||
|
||||
|
||||
|
||||
| Provider | Endpoint | Notes |
|
||||
|
||||
|---|---|---|
|
||||
|
||||
| MiniMax | `https://api.minimax.chat/v1` | Recommended default |
|
||||
|
||||
| OpenAI | `https://api.openai.com/v1` | GPT-4o works well |
|
||||
|
||||
| Mistral | `https://api.mistral.ai/v1` | Fast and cost-effective |
|
||||
|
||||
| Groq | `https://api.groq.com/openai/v1` | Very fast inference |
|
||||
|
||||
| Any other OpenAI-compatible provider | custom URL | Should work out of the box |
|
||||
|
||||
|
||||
|
||||
\---
|
||||
|
||||
|
||||
|
||||
\## Security Notes
|
||||
|
||||
|
||||
|
||||
\- The API key is stored in the Supabase database and is only accessible
|
||||
|
||||
  to Edge Functions via the `service\_role` key
|
||||
|
||||
\- The browser client never queries the `app\_settings` table directly
|
||||
|
||||
\- The `get-config-display` endpoint returns only the endpoint URL, model name,
|
||||
|
||||
  and a boolean `api\_key\_set` — the actual key is never returned
|
||||
|
||||
\- There is no user authentication (this app is intended for personal/single-user use)
|
||||
|
||||
\- If you deploy this publicly, consider adding basic auth or Supabase Auth
|
||||
|
||||
|
||||
|
||||
\---
|
||||
|
||||
|
||||
|
||||
\## License
|
||||
## Security notes
|
||||
|
||||
- The API key is held in `localStorage` and is only sent to the endpoint you
|
||||
configure. No analytics, no telemetry, no third-party calls.
|
||||
- The `MelodyMuse-config` localStorage key contains your endpoint URL, model
|
||||
name, and key. You can clear it at any time from your browser's devtools
|
||||
(Application → Local Storage).
|
||||
|
||||
## License
|
||||
|
||||
Personal use. Do whatever you want with it.
|
||||
|
||||
|
||||
|
||||
\---
|
||||
|
||||
|
||||
|
||||
\*Powered by Supabase · Made for \[Suno AI](https://suno.com)\*
|
||||
|
||||
|
||||
Reference in New Issue
Block a user