Modernize the UI and fix the equalizer sizing bug
Bug fix
- EqualizerIcon: the SVG was rendering at the wrong size (filling
its parent) because it had no explicit width/height attributes.
Now it accepts a size prop with a 24px default and the SVG element
carries the size as width/height attributes, with the className
on top. Can't accidentally balloon.
Design
- Stripped the light-mode CSS — the app is dark-only now, and the
theme toggle was a no-op (clicking it just toggled a class that
had no matching rules). ThemeToggle.tsx and lib/theme.ts are
gone; initTheme() call removed from main.tsx.
- Reworked the input form:
• brand row is now small + refined (icon + wordmark, no huge
gradient title)
• Options is a single subtle collapsible card with a chevron and
a count badge showing how many options are non-default
• Vocals is now a segmented pill (single rounded container, two
flat segments) instead of two full-width grid buttons
• Generate button is slightly smaller (py-3.5, text-sm) and
uses a softer shadow (shadow-violet-900/20)
- Empty state in the results panel: dashed-border card, soft
gradient blur behind the music icon, cleaner copy.
- Results header: smaller heading, the 'Regenerate All' and
'Cancel' buttons are now tiny ghost-style pills in the top-right.
- Sticky ZIP bar: softer shadow, more refined copy.
- Result cards: smaller icon, single-line title, the Regenerate
button is now an inline pill that fits next to Revert + Copy.
- Home page header now has a tiny equalizer icon + 'MelodyMuse'
wordmark on the left (was just text). Settings page header has
the back arrow on the left and the title, no theme toggle.
- Global CSS: added modern focus-visible ring, custom selection
color, subtle scrollbar thumb, slightly tweaked the gradient
text to a softer violet-300 → cyan-300 instead of the loud
violet-400 → fuchsia-400 → cyan-400.
This commit is contained in:
@@ -18,15 +18,15 @@ export type RegeneratingMap = Partial<Record<SectionKey | "all", boolean>>;
|
||||
|
||||
interface ResultsPanelProps {
|
||||
assets: SongAssets | null;
|
||||
originalAssets: SongAssets | null; // last *generated* values, for Revert
|
||||
loading: boolean; // initial "Generate all" skeleton
|
||||
originalAssets: SongAssets | null;
|
||||
loading: boolean;
|
||||
selectedTitleIndex: number;
|
||||
onSelectTitle: (i: number) => void;
|
||||
onChange: <K extends keyof SongAssets>(key: K, value: SongAssets[K]) => void;
|
||||
onChangeVideoPrompt: (index: number, value: string) => void;
|
||||
onRegenerateAll: () => void;
|
||||
onRegenerateSection: (section: SectionKey) => void;
|
||||
onCancel?: () => void; // cancel the in-flight generation
|
||||
onCancel?: () => void;
|
||||
anyRegenerating: boolean;
|
||||
regenerating: RegeneratingMap;
|
||||
}
|
||||
@@ -49,17 +49,17 @@ export function ResultsPanel({
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="sticky top-0 z-30 -mx-4 sm:-mx-6 lg:-mx-8 px-4 sm:px-6 lg:px-8 py-3 bg-bg/80 backdrop-blur border-b border-border mb-6 flex items-center justify-between gap-3">
|
||||
<h2 className="text-lg font-semibold text-fg">Results</h2>
|
||||
<div className="sticky top-14 z-30 -mx-4 sm:-mx-6 lg:-mx-8 px-4 sm:px-6 lg:px-8 py-3 bg-bg/80 backdrop-blur border-b border-border mb-6 flex items-center justify-between gap-3">
|
||||
<h2 className="text-sm font-semibold text-fg">Results</h2>
|
||||
<div className="flex items-center gap-1">
|
||||
{anyRegenerating && onCancel && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={onCancel}
|
||||
className="btn-ghost"
|
||||
className="px-2.5 py-1.5 rounded-md text-xs font-medium text-fg-muted hover:text-fg hover:bg-bg-hover transition-colors"
|
||||
aria-label="Cancel generation"
|
||||
>
|
||||
<span className="text-rose-300">Cancel</span>
|
||||
Cancel
|
||||
</button>
|
||||
)}
|
||||
{assets && (
|
||||
@@ -67,12 +67,12 @@ export function ResultsPanel({
|
||||
type="button"
|
||||
onClick={onRegenerateAll}
|
||||
disabled={anyRegenerating}
|
||||
className="btn-ghost"
|
||||
className="inline-flex items-center gap-1.5 px-2.5 py-1.5 rounded-md text-xs font-medium text-fg-muted hover:text-fg hover:bg-bg-hover transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
>
|
||||
<RefreshCw
|
||||
className={`w-4 h-4 ${regenerating.all ? "animate-spin" : ""}`}
|
||||
className={`w-3.5 h-3.5 ${regenerating.all ? "animate-spin" : ""}`}
|
||||
/>
|
||||
<span>Regenerate All</span>
|
||||
Regenerate all
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
@@ -136,25 +136,28 @@ export function ResultsPanel({
|
||||
|
||||
function EmptyState() {
|
||||
return (
|
||||
<div className="card p-10 text-center">
|
||||
<div className="mx-auto w-14 h-14 rounded-full gradient-bg-soft flex items-center justify-center mb-4">
|
||||
<Music className="w-7 h-7 text-violet-300" />
|
||||
<div className="flex flex-col items-center justify-center text-center px-6 py-20 rounded-2xl border border-dashed border-border">
|
||||
<div className="relative w-16 h-16 mb-5">
|
||||
<div className="absolute inset-0 rounded-full bg-gradient-to-br from-violet-500/20 to-cyan-500/20 blur-xl" />
|
||||
<div className="relative w-full h-full rounded-full bg-bg-card border border-border flex items-center justify-center">
|
||||
<Music className="w-7 h-7 text-violet-300" />
|
||||
</div>
|
||||
</div>
|
||||
<h3 className="text-base font-semibold text-fg mb-1">
|
||||
<h3 className="text-base font-semibold text-fg mb-1.5">
|
||||
Your song will appear here
|
||||
</h3>
|
||||
<p className="text-sm text-fg-muted max-w-sm mx-auto">
|
||||
<p className="text-sm text-fg-muted max-w-sm leading-relaxed">
|
||||
Describe a music idea on the left and press{" "}
|
||||
<kbd className="px-1.5 py-0.5 rounded bg-bg-hover border border-border text-[11px] font-mono">
|
||||
<kbd className="px-1.5 py-0.5 rounded bg-bg-hover border border-border text-[11px] font-mono text-fg">
|
||||
Generate
|
||||
</kbd>{" "}
|
||||
to create titles, lyrics, style, video prompts, and a YouTube
|
||||
description — all in one go.
|
||||
to get titles, lyrics, style, video prompts, and a YouTube description
|
||||
in one go.
|
||||
</p>
|
||||
<div className="mt-5 flex items-center justify-center gap-2 text-xs text-fg-muted">
|
||||
<Sparkles className="w-3.5 h-3.5" />
|
||||
Tip: open <span className="text-fg">Settings</span> to add your API key
|
||||
first.
|
||||
<div className="mt-5 flex items-center gap-1.5 text-xs text-fg-muted">
|
||||
<Sparkles className="w-3 h-3" />
|
||||
First time? Open <span className="text-fg">Settings</span> to add your
|
||||
API key.
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user