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:
2026-06-03 01:25:22 +02:00
parent f2401d0c57
commit d42410560f
37 changed files with 5443 additions and 376 deletions
+87 -376
View File
@@ -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 (
&#x20; id INT PRIMARY KEY DEFAULT 1,
&#x20; api\_endpoint TEXT NOT NULL DEFAULT '',
&#x20; api\_key TEXT NOT NULL DEFAULT '',
&#x20; model\_name TEXT NOT NULL DEFAULT '',
&#x20; 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:
&#x20; - \*\*API Endpoint URL\*\* — e.g. `https://api.minimax.chat/v1`
&#x20; - \*\*API Key\*\* — your provider's secret key
&#x20; - \*\*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
&#x20; > \_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
&#x20; \_(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
&#x20; \_(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
&#x20; 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,
&#x20; 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)\*