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.
This commit is contained in:
2026-06-03 01:47:11 +02:00
parent d42410560f
commit b945417773
16 changed files with 991 additions and 282 deletions
+38 -8
View File
@@ -11,6 +11,18 @@ 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 shortcut**`Cmd/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
@@ -27,15 +39,29 @@ MelodyMuse/
│ ├── main.tsx
│ ├── index.css # Tailwind layers + design-system components
│ ├── components/ # UI building blocks
│ │ ── cards/ # The five result cards
│ │ ── 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
│ │ ├── types.ts # shared TypeScript types
│ │ ├── zip.ts # JSZip layout
│ │ ├── theme.ts # dark/light mode
│ │ ── toast.tsx # toast context
│ │ ── 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
@@ -68,10 +94,11 @@ MelodyMuse/
- **Model Name** — e.g. `MiniMax-M3`
Click **Save Configuration**, then **Test Connection** to confirm
everything is wired up.
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**.
**Generate Song Assets** (or press `Cmd/Ctrl + Enter`).
## Build for production
@@ -112,8 +139,11 @@ The configured endpoint must expose an OpenAI-compatible
- 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
- 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