Files
MelodyMuse/README.md
T
Jannik b945417773 Add cancel, history, revert, and quality-of-life features
UX

- Add elapsed-time counter and Cancel button to in-flight generations,
  wired through AbortController so partial responses are discarded
  cleanly. The cancel action is available from both the input panel
  and the results header.
- Add a Recent generations panel below the input. The last 6 successful
  generations are saved to localStorage; one click reloads both the
  input fields and the generated assets.
- Add a Revert button to every editable card. It appears the moment
  the current value diverges from the last generated value and restores
  the field with a single click.
- Add an Empty state to the right panel with a friendly hint pointing
  at the Generate button and the Settings page.
- Add a 'Try an example' button that fills the input with a random
  starter idea (idea + mood + vocals).
- Add Cmd/Ctrl+Enter as a keyboard shortcut to generate.
- Add a Footer with project info and a privacy reminder.
- Add a 'Clear saved key' button to the Settings page so the user can
  remove the API key without overwriting it.

Bug fix

- Settings > Test Connection used to save the in-progress form values
  to localStorage before testing. It now uses the in-memory candidate
  config, so failed tests don't pollute the saved config.

Code quality

- Extract the duplicated useAutoHeight hook to src/lib/useAutoHeight.ts.
- Extract a useElapsed hook for the loading timer.
- Move InputValues into src/lib/types.ts (was duplicated in
  InputPanel.tsx) and add SECTION_LABELS, replacing the humanizeSection
  switch in HomePage.
- Centralize the filename sanitization: HomePage now calls
  sanitizeFilename from zip.ts instead of duplicating the regex.
- Add previewConfig / testConnectionWithConfig helpers to llm.ts to
  support in-memory connection tests.
2026-06-03 01:47:11 +02:00

5.6 KiB

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.

Features

  • 🎵 All 5 Suno assets in one click — titles, lyrics, style, video prompts, YouTube description
  • ✏️ Inline editing — every generated field is editable; revert any edit with one click
  • 🔄 Per-section regeneration — regenerate just one section; the rest of the song is passed as context for consistency
  • Elapsed-time counter + Cancel button for long generations
  • 🕘 Recent generations — last 6 are kept in localStorage; one click to reload
  • ⌨️ Keyboard shortcutCmd/Ctrl + Enter to generate
  • 🎲 Try an example — fill the input with a random sample idea
  • 🌗 Dark / light mode — toggle in the header, persisted
  • 💾 Standalone — no backend, no signup, no telemetry

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/
│   ├── App.tsx
│   ├── main.tsx
│   ├── index.css                # Tailwind layers + design-system components
│   ├── components/              # UI building blocks
│   │   ├── cards/               # The five result cards
│   │   ├── ConfigBanner.tsx
│   │   ├── CopyButton.tsx
│   │   ├── EqualizerIcon.tsx
│   │   ├── Footer.tsx
│   │   ├── HistoryPanel.tsx
│   │   ├── InputPanel.tsx
│   │   ├── ResultCard.tsx
│   │   ├── ResultsPanel.tsx
│   │   ├── SkeletonCard.tsx
│   │   ├── StickyZipBar.tsx
│   │   └── ThemeToggle.tsx
│   ├── pages/                   # HomePage, SettingsPage
│   ├── lib/
│   │   ├── history.ts           # recent-generations persistence
│   │   ├── llm.ts               # direct fetch → provider, JSON extraction, config persistence
│   │   ├── prompts.ts           # system + user prompt construction
│   │   ├── theme.ts             # dark/light mode
│   │   ├── toast.tsx            # toast context
│   │   ├── types.ts             # shared TypeScript types + SECTION_LABELS
│   │   ├── useAutoHeight.ts     # shared textarea auto-grow hook
│   │   ├── useElapsed.ts        # shared "X seconds elapsed" hook
│   │   └── zip.ts               # JSZip layout
│   └── vite-env.d.ts
├── public/favicon.svg
├── index.html
├── tailwind.config.js
├── vite.config.ts
├── tsconfig*.json
└── package.json

Getting started

  1. Install dependencies

    npm install
    
  2. Start the dev server

    npm run dev
    

    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

    Click Save Configuration, then Test Connection to confirm everything is wired up. Test Connection uses your unsaved form values — your changes are only persisted when you click Save.

  4. Generate. Return to the home page, type a music idea, click Generate Song Assets (or press Cmd/Ctrl + Enter).

Build for production

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.

CORS

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:

  • 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 } }] }.

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.
  • Three localStorage keys are used:
    • melodymuse-config{ api_endpoint, api_key, model_name }
    • melodymuse-history — the last 6 generations (input + assets)
    • melodymuse-theme'dark' | 'light'
  • You can clear them at any time from your browser's devtools (Application → Local Storage).

License

Personal use. Do whatever you want with it.