From 3096669a13697fd25bc81b09425a586ca9df3d9e Mon Sep 17 00:00:00 2001
From: orfelorfel23
Date: Wed, 3 Jun 2026 08:59:31 +0200
Subject: [PATCH] Polish: draft autosave, history refresh, outdated footer,
more
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Bug fixes / UX
- Footer: the privacy line still claimed 'generation runs in your
browser'. Now correctly says the generation runs on the server and
the key never reaches the browser.
- History panel: when the panel was open while a new generation
completed, the new entry didn't appear until the user closed and
re-opened the panel. The history module now dispatches a custom
'melodymuse-history-updated' event on every write; the panel
listens and re-reads. removeFromHistory/clearHistory emit too.
writeAll is also now try/catch'd against quota errors.
- Auto-save the input form: the input is persisted to localStorage
('melodymuse-draft', debounced 400ms) so a refresh doesn't wipe
what the user was typing. The initial state hydrates from the
draft on mount.
- Cmd/Ctrl+Enter now works in the Music style field as well as
the idea field — shared onKeyDown handler, propagated through the
StyleField sub-component.
- Full regen ('Generate' / 'Regenerate All') now clears the previous
assets before kicking off the new request, so the skeleton state
shows during the round-trip instead of stale cards with a spinner
on top. Per-section regen still leaves the other cards intact.
- Settings → Local data: the in-progress draft is now listed
alongside history and theme, and has its own 'Clear in-progress
draft' button. The 'Clear all local data' button clears it too.
---
src/components/Footer.tsx | 4 +--
src/components/HistoryPanel.tsx | 29 ++++++++++++++-------
src/components/InputPanel.tsx | 12 ++++++++-
src/lib/history.ts | 43 ++++++++++++++++++++++---------
src/pages/HomePage.tsx | 45 +++++++++++++++++++++++++++++++--
src/pages/SettingsPage.tsx | 38 ++++++++++++++++++++++------
6 files changed, 137 insertions(+), 34 deletions(-)
diff --git a/src/components/Footer.tsx b/src/components/Footer.tsx
index b0b8324..f11faac 100644
--- a/src/components/Footer.tsx
+++ b/src/components/Footer.tsx
@@ -7,8 +7,8 @@ export function Footer() {
Generate Suno song assets with AI
- All generation runs in your browser — your API key never leaves this
- device.
+ Generation runs on the MelodyMuse server — your API key never reaches
+ the browser.
diff --git a/src/components/HistoryPanel.tsx b/src/components/HistoryPanel.tsx
index 429e80a..91482fc 100644
--- a/src/components/HistoryPanel.tsx
+++ b/src/components/HistoryPanel.tsx
@@ -1,13 +1,14 @@
-import { useEffect, useState } from 'react';
-import { Clock, History, Trash2, X } from 'lucide-react';
+import { useEffect, useState } from "react";
+import { Clock, History, Trash2, X } from "lucide-react";
import {
clearHistory,
formatRelative,
+ HISTORY_UPDATED_EVENT,
loadHistory,
removeFromHistory,
type HistoryEntry,
-} from '../lib/history';
-import type { InputValues } from '../lib/types';
+} from "../lib/history";
+import type { InputValues } from "../lib/types";
interface HistoryPanelProps {
onLoad: (entry: HistoryEntry) => void;
@@ -17,13 +18,23 @@ 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.
+ // Refresh the list whenever the panel is opened.
useEffect(() => {
if (!open) return;
setEntries(loadHistory());
}, [open]);
+ // And whenever the history is updated from elsewhere in the app (after a
+ // successful generation, or a delete/clear from a different panel).
+ useEffect(() => {
+ const handler = () => {
+ if (!open) return;
+ setEntries(loadHistory());
+ };
+ window.addEventListener(HISTORY_UPDATED_EVENT, handler);
+ return () => window.removeEventListener(HISTORY_UPDATED_EVENT, handler);
+ }, [open]);
+
if (entries.length === 0 && !open) {
return null;
}
@@ -43,7 +54,7 @@ export function HistoryPanel({ onLoad }: HistoryPanelProps) {
({entries.length})
)}
- {open ? 'Hide' : 'Show'}
+ {open ? "Hide" : "Show"}
{open && (
@@ -130,6 +141,6 @@ function HistoryRow({
function summarizeInput(v: InputValues): string {
const idea = v.idea.trim();
const max = 90;
- if (idea.length <= max) return idea || '(no idea text)';
- return idea.slice(0, max - 1) + '…';
+ if (idea.length <= max) return idea || "(no idea text)";
+ return idea.slice(0, max - 1) + "…";
}
diff --git a/src/components/InputPanel.tsx b/src/components/InputPanel.tsx
index 0555469..0dca226 100644
--- a/src/components/InputPanel.tsx
+++ b/src/components/InputPanel.tsx
@@ -228,6 +228,7 @@ export function InputPanel({
loading={styleLoading}
onChange={(v) => set("style_hint", v)}
onRandom={handleStyleRandom}
+ onCmdEnter={onKeyDown}
/>