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} />
@@ -324,7 +325,15 @@ interface StyleFieldProps { onRandom: (mode: "normal" | "crazy") => void; } -function StyleField({ value, loading, onChange, onRandom }: StyleFieldProps) { +function StyleField({ + value, + loading, + onChange, + onRandom, + onCmdEnter, +}: StyleFieldProps & { + onCmdEnter: (e: KeyboardEvent) => void; +}) { return (