feat: complete UI/UX overhaul with Impeccable design system

Replace Inter/Baskerville with self-hosted Manrope/Alegreya/JetBrains Mono
variable fonts. Migrate all colors from hex to OKLCH tokens (30+ CSS custom
properties) with full dark/light mode support. Replace Unicode emoji with
lucide-react SVG icons throughout. Convert all page layouts to inline styles
(Tailwind CSS 4 flex/grid classes unreliable in this project). Code-split
routes via React.lazy (760KB → 15KB initial shell + 10 lazy chunks).

Add global styles: scrollbar theming, selection color, input/button bases,
:focus-visible ring, prefers-reduced-motion. Setup wizard gets 4-phase
indicator with numbered circles, PathInput and StatusDot components.
Toast container gets aria-live="polite". Tab close buttons changed to
proper <button> elements with aria-labels.

All 8 pages (Dashboard, Editor, Build, Server, Toolset, Repos, Settings,
Setup) rewritten with consistent card/section/button patterns.
This commit is contained in:
plenarius
2026-04-21 03:06:29 -04:00
parent 8b35c41a52
commit cbe51a6e67
29 changed files with 3531 additions and 1206 deletions
+102 -47
View File
@@ -3,14 +3,28 @@ import { Outlet, Link, useLocation, useNavigate } from "react-router-dom";
import { Terminal } from "../components/terminal/Terminal";
import { useWebSocket } from "../hooks/useWebSocket";
import { useTheme } from "../hooks/useTheme";
import {
Code2,
Wrench,
Hammer,
Play,
GitBranch,
Settings,
Sun,
Moon,
Terminal as TerminalIcon,
ChevronDown,
ChevronUp,
} from "lucide-react";
import type { LucideIcon } from "lucide-react";
const NAV_ITEMS = [
{ path: "/editor", label: "Editor", icon: "\u270E" },
{ path: "/toolset", label: "Toolset", icon: "\u2699" },
{ path: "/build", label: "Build", icon: "\u2692" },
{ path: "/server", label: "Server", icon: "\u25B6" },
{ path: "/repos", label: "Repos", icon: "\u2387" },
{ path: "/settings", label: "Settings", icon: "\u2318" },
const NAV_ITEMS: { path: string; label: string; Icon: LucideIcon }[] = [
{ path: "/editor", label: "Editor", Icon: Code2 },
{ path: "/toolset", label: "Toolset", Icon: Wrench },
{ path: "/build", label: "Build", Icon: Hammer },
{ path: "/server", label: "Server", Icon: Play },
{ path: "/repos", label: "Repos", Icon: GitBranch },
{ path: "/settings", label: "Settings", Icon: Settings },
];
export function IDELayout({ sidebar }: { sidebar?: React.ReactNode }) {
@@ -21,6 +35,7 @@ export function IDELayout({ sidebar }: { sidebar?: React.ReactNode }) {
const navigate = useNavigate();
const { subscribe } = useWebSocket();
const { theme, toggleTheme } = useTheme();
const showSidebar = location.pathname === "/editor" || location.pathname.startsWith("/editor/");
useEffect(() => {
return subscribe("git:upstream-update", (event) => {
@@ -85,25 +100,28 @@ export function IDELayout({ sidebar }: { sidebar?: React.ReactNode }) {
};
return (
<div className="flex h-screen overflow-hidden" style={{ backgroundColor: "var(--forge-bg)" }}>
<div style={{ display: "flex", height: "100vh", overflow: "hidden", backgroundColor: "var(--forge-bg)" }}>
{/* Left sidebar nav */}
<nav
className="flex shrink-0 flex-col"
aria-label="Main navigation"
style={{
display: "flex",
flexDirection: "column",
width: "56px",
flexShrink: 0,
borderRight: "1px solid var(--forge-border)",
backgroundColor: "var(--forge-surface)",
}}
>
<Link
to="/"
className="flex items-center justify-center py-3"
style={{ display: "flex", alignItems: "center", justifyContent: "center", padding: "0.75rem 0", textDecoration: "none" }}
title="Dashboard"
>
<img src="/layonara.png" alt="Layonara" style={{ width: "40px" }} />
<img src="/layonara.png" alt="Layonara" style={{ width: "36px" }} />
</Link>
<div className="mt-2 flex flex-1 flex-col">
<div style={{ marginTop: "0.25rem", display: "flex", flexDirection: "column", flex: 1 }}>
{NAV_ITEMS.map((item) => {
const isActive =
item.path === "/"
@@ -115,20 +133,26 @@ export function IDELayout({ sidebar }: { sidebar?: React.ReactNode }) {
<Link
key={item.path}
to={item.path}
className="relative flex flex-col items-center justify-center py-2.5 text-center transition-colors hover:bg-white/5"
style={{
borderLeft: isActive
? "3px solid var(--forge-accent)"
: "3px solid transparent",
backgroundColor: isActive ? "rgba(148, 98, 0, 0.1)" : undefined,
display: "flex",
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
padding: "0.625rem 0",
position: "relative",
textDecoration: "none",
transition: "background-color 150ms, color 150ms",
backgroundColor: isActive ? "var(--forge-accent-subtle)" : undefined,
color: isActive ? "var(--forge-accent)" : "var(--forge-text-secondary)",
}}
onMouseEnter={(e) => { if (!isActive) e.currentTarget.style.backgroundColor = "var(--forge-surface-raised)"; }}
onMouseLeave={(e) => { if (!isActive) e.currentTarget.style.backgroundColor = isActive ? "var(--forge-accent-subtle)" : ""; }}
title={item.label}
>
<span className="text-base">{item.icon}</span>
<span className="mt-0.5 text-[9px] leading-tight">{item.label}</span>
<item.Icon size={18} strokeWidth={isActive ? 2.5 : 2} />
<span style={{ marginTop: "0.25rem", fontSize: "0.625rem", fontWeight: 500, lineHeight: 1 }}>{item.label}</span>
{badge > 0 && (
<span className="absolute right-1 top-1 flex h-3.5 min-w-3.5 items-center justify-center rounded-full bg-amber-500 px-0.5 text-[8px] font-bold text-black">
<span className="absolute right-1 top-1 flex h-3.5 min-w-3.5 items-center justify-center rounded-full px-0.5 text-[8px] font-bold" style={{ backgroundColor: "var(--forge-accent)", color: "var(--forge-accent-text)" }}>
{badge}
</span>
)}
@@ -139,69 +163,100 @@ export function IDELayout({ sidebar }: { sidebar?: React.ReactNode }) {
<button
onClick={toggleTheme}
className="flex items-center justify-center py-3 text-sm transition-colors hover:bg-white/5"
style={{ color: "var(--forge-text-secondary)" }}
style={{
display: "flex",
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
padding: "0.625rem 0",
color: "var(--forge-text-secondary)",
background: "none",
border: "none",
width: "100%",
transition: "background-color 150ms, color 150ms",
}}
title={`Switch to ${theme === "dark" ? "light" : "dark"} mode`}
onMouseEnter={(e) => { e.currentTarget.style.backgroundColor = "var(--forge-surface-raised)"; }}
onMouseLeave={(e) => { e.currentTarget.style.backgroundColor = ""; }}
>
{theme === "dark" ? "\u2600\uFE0F" : "\uD83C\uDF19"}
{theme === "dark" ? <Sun size={18} /> : <Moon size={18} />}
<span style={{ marginTop: "0.25rem", fontSize: "0.625rem", fontWeight: 500, lineHeight: 1 }}>{theme === "dark" ? "Light" : "Dark"}</span>
</button>
</nav>
{/* Main content area */}
<div className="flex flex-1 flex-col overflow-hidden">
<div style={{ display: "flex", flexDirection: "column", flex: 1, overflow: "hidden" }}>
<header
className="flex shrink-0 items-center gap-4 px-4 py-1.5"
style={{ borderBottom: "1px solid var(--forge-border)" }}
style={{
display: "flex",
alignItems: "center",
gap: "1rem",
padding: "0.375rem 1rem",
borderBottom: "1px solid var(--forge-border)",
flexShrink: 0,
}}
>
<div className="flex items-center gap-2">
<span
className="text-lg font-bold"
style={{
fontFamily: "'Baskerville', 'Georgia', 'Palatino', serif",
color: "var(--forge-accent)",
}}
>
Layonara Forge
</span>
</div>
<div className="flex-1" />
<span
style={{
fontFamily: "var(--font-heading)",
fontSize: "var(--text-lg)",
fontWeight: 700,
color: "var(--forge-accent)",
}}
>
Layonara Forge
</span>
</header>
<div className="flex flex-1 overflow-hidden">
{sidebar && (
<div style={{ display: "flex", flex: 1, overflow: "hidden" }}>
{sidebar && showSidebar && (
<aside
className="shrink-0 overflow-hidden"
style={{
width: "250px",
flexShrink: 0,
overflow: "hidden",
borderRight: "1px solid var(--forge-border)",
}}
>
{sidebar}
</aside>
)}
<main className="flex-1 overflow-hidden">
<main style={{ flex: 1, overflow: "hidden" }}>
<Outlet />
</main>
</div>
<button
onClick={() => setTerminalOpen((v) => !v)}
className="flex shrink-0 items-center gap-1 px-3 py-0.5 text-xs transition-colors hover:bg-white/5"
style={{
borderTop: "1px solid var(--forge-border)",
display: "flex",
alignItems: "center",
gap: "0.5rem",
padding: "0.375rem 0.75rem",
color: "var(--forge-text-secondary)",
fontFamily: "'JetBrains Mono', 'Fira Code', monospace",
fontFamily: "var(--font-mono)",
fontSize: "var(--text-xs)",
background: "none",
border: "none",
borderTop: "1px solid var(--forge-border)",
width: "100%",
cursor: "pointer",
transition: "background-color 150ms",
}}
onMouseEnter={(e) => { e.currentTarget.style.backgroundColor = "var(--forge-surface-raised)"; }}
onMouseLeave={(e) => { e.currentTarget.style.backgroundColor = ""; }}
>
<span>{terminalOpen ? "\u25BC" : "\u25B2"}</span>
<TerminalIcon size={12} />
<span>Terminal</span>
{terminalOpen ? <ChevronDown size={12} /> : <ChevronUp size={12} />}
</button>
{terminalOpen && (
<div
className="shrink-0 overflow-hidden"
style={{
height: "300px",
flexShrink: 0,
overflow: "hidden",
borderTop: "1px solid var(--forge-border)",
}}
>