feat: complete UI overhaul (liquid glass theme, i18n, FontAwesome)
This commit is contained in:
+31
-40
@@ -1,16 +1,18 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { Link, useNavigate } from "react-router-dom";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import {
|
||||
ArrowLeft,
|
||||
CheckCircle2,
|
||||
AlertTriangle,
|
||||
Loader2,
|
||||
Plug,
|
||||
Trash2,
|
||||
Server,
|
||||
} from "lucide-react";
|
||||
faArrowLeft,
|
||||
faCheckCircle,
|
||||
faExclamationTriangle,
|
||||
faCircleNotch,
|
||||
faPlug,
|
||||
faTrashAlt,
|
||||
faServer,
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
import { useToast } from "../lib/toast";
|
||||
import { getServerStatus, type ServerStatus } from "../lib/llm";
|
||||
import { useI18n } from "../lib/i18n";
|
||||
|
||||
const HISTORY_KEY = "melodymuse-history";
|
||||
const DRAFT_KEY = "melodymuse-draft";
|
||||
@@ -44,6 +46,7 @@ function readLocalDataSummary(): LocalDataSummary {
|
||||
export function SettingsPage() {
|
||||
const toast = useToast();
|
||||
const navigate = useNavigate();
|
||||
const { t } = useI18n();
|
||||
|
||||
const [status, setStatus] = useState<ServerStatus | null>(null);
|
||||
const [statusError, setStatusError] = useState<string | null>(null);
|
||||
@@ -100,7 +103,7 @@ export function SettingsPage() {
|
||||
const onClearAll = () => {
|
||||
if (
|
||||
!window.confirm(
|
||||
"Clear all MelodyMuse data from this browser? This will remove the recent generations, the in-progress draft, and the theme preference. The API key on the server is NOT affected.",
|
||||
"Clear all MelodyMuse data from this browser? This will remove the recent generations, the in-progress draft, and the theme preference.",
|
||||
)
|
||||
)
|
||||
return;
|
||||
@@ -121,9 +124,9 @@ export function SettingsPage() {
|
||||
className="p-2 -ml-2 rounded-md text-fg-muted hover:text-fg hover:bg-bg-hover transition-colors"
|
||||
aria-label="Back"
|
||||
>
|
||||
<ArrowLeft className="w-5 h-5" />
|
||||
<FontAwesomeIcon icon={faArrowLeft} className="w-4 h-4" />
|
||||
</button>
|
||||
<h1 className="text-sm font-semibold text-fg">Settings</h1>
|
||||
<h1 className="text-sm font-semibold text-fg">{t.settings}</h1>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
@@ -131,7 +134,7 @@ export function SettingsPage() {
|
||||
<div className="card p-6 space-y-6">
|
||||
<section>
|
||||
<h2 className="text-sm font-semibold text-fg mb-3 flex items-center gap-2">
|
||||
<Server className="w-4 h-4" />
|
||||
<FontAwesomeIcon icon={faServer} className="w-4 h-4 text-accent-primary" />
|
||||
Server status
|
||||
</h2>
|
||||
|
||||
@@ -158,7 +161,7 @@ export function SettingsPage() {
|
||||
</p>
|
||||
)}
|
||||
{statusError && !checking && (
|
||||
<p className="text-xs text-rose-300 break-words">
|
||||
<p className="text-xs text-rose-400 break-words">
|
||||
{statusError}
|
||||
</p>
|
||||
)}
|
||||
@@ -171,21 +174,13 @@ export function SettingsPage() {
|
||||
className="btn-ghost"
|
||||
>
|
||||
{checking ? (
|
||||
<Loader2 className="w-4 h-4 animate-spin" />
|
||||
<FontAwesomeIcon icon={faCircleNotch} spin className="w-4 h-4" />
|
||||
) : (
|
||||
<Plug className="w-4 h-4" />
|
||||
<FontAwesomeIcon icon={faPlug} className="w-4 h-4" />
|
||||
)}
|
||||
<span>Re-check</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<p className="text-xs text-fg-muted">
|
||||
The LLM provider's API key lives on this server in the{" "}
|
||||
<code className="px-1 py-0.5 rounded bg-bg-hover border border-border font-mono">
|
||||
LLM_API_KEY
|
||||
</code>{" "}
|
||||
environment variable. The browser never sees it.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -199,16 +194,16 @@ export function SettingsPage() {
|
||||
</p>
|
||||
<ul className="text-xs text-fg-muted space-y-1 mb-4">
|
||||
<li>
|
||||
<code className="font-mono">melodymuse-history</code> —{" "}
|
||||
<code className="font-mono text-accent-primary">melodymuse-history</code> —{" "}
|
||||
{summary.historyEntries} recent generation
|
||||
{summary.historyEntries === 1 ? "" : "s"}
|
||||
</li>
|
||||
<li>
|
||||
<code className="font-mono">melodymuse-draft</code> —{" "}
|
||||
<code className="font-mono text-accent-primary">melodymuse-draft</code> —{" "}
|
||||
{summary.hasDraft ? "saved (in-progress input)" : "empty"}
|
||||
</li>
|
||||
<li>
|
||||
<code className="font-mono">melodymuse-theme</code> —{" "}
|
||||
<code className="font-mono text-accent-primary">melodymuse-theme</code> —{" "}
|
||||
{summary.hasTheme ? "set" : "using default"}
|
||||
</li>
|
||||
</ul>
|
||||
@@ -219,7 +214,7 @@ export function SettingsPage() {
|
||||
disabled={summary.historyEntries === 0}
|
||||
className="btn-secondary"
|
||||
>
|
||||
<Trash2 className="w-4 h-4" />
|
||||
<FontAwesomeIcon icon={faTrashAlt} className="w-4 h-4" />
|
||||
Clear recent generations
|
||||
</button>
|
||||
<button
|
||||
@@ -228,30 +223,26 @@ export function SettingsPage() {
|
||||
disabled={!summary.hasDraft}
|
||||
className="btn-secondary"
|
||||
>
|
||||
<Trash2 className="w-4 h-4" />
|
||||
<FontAwesomeIcon icon={faTrashAlt} className="w-4 h-4" />
|
||||
Clear in-progress draft
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={onClearAll}
|
||||
className="btn-secondary text-rose-300 hover:text-rose-200"
|
||||
className="btn-secondary text-rose-400 hover:text-rose-300"
|
||||
>
|
||||
<Trash2 className="w-4 h-4" />
|
||||
<FontAwesomeIcon icon={faTrashAlt} className="w-4 h-4" />
|
||||
Clear all local data
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<p className="text-center text-xs text-fg-muted">
|
||||
API key on the server · Recent generations in this browser only
|
||||
</p>
|
||||
|
||||
<div className="text-center">
|
||||
<div className="text-center pt-2">
|
||||
<Link
|
||||
to="/"
|
||||
className="text-xs text-fg-muted hover:text-fg transition-colors"
|
||||
className="text-xs font-semibold text-fg-muted hover:text-accent-primary transition-colors flex items-center justify-center gap-2"
|
||||
>
|
||||
← Back to generator
|
||||
<FontAwesomeIcon icon={faArrowLeft} /> Back to generator
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
@@ -262,7 +253,7 @@ export function SettingsPage() {
|
||||
|
||||
function StatusDot({ checking, ok }: { checking: boolean; ok: boolean }) {
|
||||
if (checking)
|
||||
return <Loader2 className="w-5 h-5 text-fg-muted animate-spin shrink-0" />;
|
||||
if (ok) return <CheckCircle2 className="w-5 h-5 text-emerald-400 shrink-0" />;
|
||||
return <AlertTriangle className="w-5 h-5 text-rose-400 shrink-0" />;
|
||||
return <FontAwesomeIcon icon={faCircleNotch} spin className="w-5 h-5 text-fg-muted shrink-0" />;
|
||||
if (ok) return <FontAwesomeIcon icon={faCheckCircle} className="w-5 h-5 text-emerald-400 shrink-0" />;
|
||||
return <FontAwesomeIcon icon={faExclamationTriangle} className="w-5 h-5 text-rose-400 shrink-0" />;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user