diff --git a/README.md b/README.md
index 529cdcf..d57a353 100644
--- a/README.md
+++ b/README.md
@@ -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
diff --git a/src/components/Footer.tsx b/src/components/Footer.tsx
new file mode 100644
index 0000000..b0b8324
--- /dev/null
+++ b/src/components/Footer.tsx
@@ -0,0 +1,16 @@
+export function Footer() {
+ return (
+
+ );
+}
diff --git a/src/components/HistoryPanel.tsx b/src/components/HistoryPanel.tsx
new file mode 100644
index 0000000..429e80a
--- /dev/null
+++ b/src/components/HistoryPanel.tsx
@@ -0,0 +1,135 @@
+import { useEffect, useState } from 'react';
+import { Clock, History, Trash2, X } from 'lucide-react';
+import {
+ clearHistory,
+ formatRelative,
+ loadHistory,
+ removeFromHistory,
+ type HistoryEntry,
+} from '../lib/history';
+import type { InputValues } from '../lib/types';
+
+interface HistoryPanelProps {
+ onLoad: (entry: HistoryEntry) => void;
+}
+
+export function HistoryPanel({ onLoad }: HistoryPanelProps) {
+ const [open, setOpen] = useState(false);
+ const [entries, setEntries] = useState([]);
+
+ // Refresh the list whenever the panel is opened, so newly-saved generations
+ // appear without needing a full page refresh.
+ useEffect(() => {
+ if (!open) return;
+ setEntries(loadHistory());
+ }, [open]);
+
+ if (entries.length === 0 && !open) {
+ return null;
+ }
+
+ return (
+