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
+214 -70
View File
@@ -1,6 +1,17 @@
import { useState, useEffect, useRef, useCallback } from "react";
import { api } from "../services/api";
import { useWebSocket } from "../hooks/useWebSocket";
import {
Hammer,
Package,
Cpu,
Play,
Archive,
Upload,
ChevronDown,
ChevronUp,
AlertTriangle,
} from "lucide-react";
type BuildStatus = "idle" | "building" | "success" | "failed";
@@ -11,15 +22,38 @@ interface BuildSectionState {
}
function StatusBadge({ status }: { status: BuildStatus }) {
const colors: Record<BuildStatus, string> = {
idle: "bg-gray-500/20 text-gray-400",
building: "bg-yellow-500/20 text-yellow-400",
success: "bg-green-500/20 text-green-400",
failed: "bg-red-500/20 text-red-400",
const styles: Record<BuildStatus, React.CSSProperties> = {
idle: {
backgroundColor: "var(--forge-surface-raised)",
color: "var(--forge-text-secondary)",
},
building: {
backgroundColor: "var(--forge-warning-bg)",
color: "var(--forge-warning)",
},
success: {
backgroundColor: "var(--forge-success-bg)",
color: "var(--forge-success)",
},
failed: {
backgroundColor: "var(--forge-danger-bg)",
color: "var(--forge-danger)",
},
};
return (
<span className={`rounded-full px-2 py-0.5 text-xs font-medium ${colors[status]}`}>
<span
style={{
...styles[status],
borderRadius: "9999px",
padding: "0.125rem 0.625rem",
fontSize: "var(--text-xs)",
fontWeight: 600,
fontFamily: "var(--font-mono)",
textTransform: "uppercase" as const,
letterSpacing: "0.03em",
}}
>
{status}
</span>
);
@@ -43,31 +77,47 @@ function BuildOutput({
}, [lines, collapsed]);
return (
<div className="mt-2">
<div style={{ marginTop: "0.75rem" }}>
<button
onClick={onToggle}
className="flex items-center gap-1 text-xs transition-colors hover:opacity-80"
style={{ color: "var(--forge-text-secondary)" }}
style={{
display: "flex",
alignItems: "center",
gap: "0.375rem",
fontSize: "var(--text-xs)",
color: "var(--forge-text-secondary)",
background: "none",
border: "none",
cursor: "pointer",
padding: "0.25rem 0",
fontFamily: "var(--font-sans)",
}}
>
<span>{collapsed ? "\u25B6" : "\u25BC"}</span>
{collapsed ? <ChevronDown size={14} /> : <ChevronUp size={14} />}
<span>Output ({lines.length} lines)</span>
</button>
{!collapsed && (
<div
ref={scrollRef}
className="mt-1 max-h-64 overflow-auto rounded p-3"
style={{
backgroundColor: "#0d1117",
fontFamily: "'JetBrains Mono', 'Fira Code', monospace",
fontSize: "12px",
lineHeight: "1.5",
marginTop: "0.5rem",
maxHeight: "16rem",
overflowY: "auto",
borderRadius: "0.5rem",
padding: "0.875rem 1rem",
backgroundColor: "var(--forge-log-bg)",
fontFamily: "var(--font-mono)",
fontSize: "var(--text-xs)",
lineHeight: "1.6",
}}
>
{lines.length === 0 ? (
<span style={{ color: "var(--forge-text-secondary)" }}>No output yet</span>
<span style={{ color: "var(--forge-text-secondary)", fontStyle: "italic" }}>
No output yet
</span>
) : (
lines.map((line, i) => (
<div key={i} style={{ color: "#c9d1d9" }}>
<div key={i} style={{ color: "var(--forge-log-text)" }}>
{line}
</div>
))
@@ -83,27 +133,29 @@ function ActionButton({
onClick,
disabled,
variant = "default",
icon,
}: {
label: string;
onClick: () => void;
disabled?: boolean;
variant?: "default" | "primary" | "warning";
icon?: React.ReactNode;
}) {
const styles = {
const variantStyles: Record<string, React.CSSProperties> = {
default: {
backgroundColor: "var(--forge-surface)",
borderColor: "var(--forge-border)",
backgroundColor: "var(--forge-surface-raised)",
border: "1px solid var(--forge-border)",
color: "var(--forge-text)",
},
primary: {
backgroundColor: "var(--forge-accent)",
borderColor: "var(--forge-accent)",
color: "#fff",
border: "none",
color: "var(--forge-accent-text)",
},
warning: {
backgroundColor: "#854d0e",
borderColor: "#a16207",
color: "#fef08a",
backgroundColor: "var(--forge-warning-bg)",
border: "1px solid var(--forge-warning-border)",
color: "var(--forge-warning)",
},
};
@@ -111,9 +163,22 @@ function ActionButton({
<button
onClick={onClick}
disabled={disabled}
className="rounded border px-3 py-1.5 text-sm font-medium transition-opacity disabled:cursor-not-allowed disabled:opacity-50"
style={styles[variant]}
style={{
...variantStyles[variant],
borderRadius: "0.375rem",
padding: "0.5rem 1rem",
fontSize: "var(--text-sm)",
fontWeight: 600,
fontFamily: "var(--font-sans)",
cursor: disabled ? "not-allowed" : "pointer",
opacity: disabled ? 0.5 : 1,
display: "inline-flex",
alignItems: "center",
gap: "0.375rem",
transition: "opacity 0.15s ease",
}}
>
{icon}
{label}
</button>
);
@@ -191,41 +256,103 @@ export function Build() {
[],
);
const isBuilding = module.status === "building" || haks.status === "building" || nwnx.status === "building";
const isBuilding =
module.status === "building" || haks.status === "building" || nwnx.status === "building";
const cardStyle: React.CSSProperties = {
backgroundColor: "var(--forge-surface)",
border: "1px solid var(--forge-border)",
borderRadius: "0.75rem",
padding: "1.25rem",
marginBottom: "1rem",
};
const sectionHeaderStyle: React.CSSProperties = {
display: "flex",
alignItems: "center",
justifyContent: "space-between",
marginBottom: "1rem",
};
const sectionTitleStyle: React.CSSProperties = {
display: "flex",
alignItems: "center",
gap: "0.5rem",
textTransform: "uppercase",
fontSize: "var(--text-xs)",
fontWeight: 600,
letterSpacing: "0.05em",
color: "var(--forge-text-secondary)",
fontFamily: "var(--font-heading)",
};
const buttonRowStyle: React.CSSProperties = {
display: "flex",
flexWrap: "wrap",
gap: "0.5rem",
alignItems: "center",
};
return (
<div className="h-full overflow-auto p-6" style={{ color: "var(--forge-text)" }}>
<h2 className="mb-6 text-2xl font-bold" style={{ color: "var(--forge-accent)" }}>
Build Pipeline
</h2>
<div
style={{
height: "100%",
overflowY: "auto",
padding: "1.5rem",
color: "var(--forge-text)",
}}
>
<div style={{ marginBottom: "1.5rem" }}>
<h2
style={{
fontFamily: "var(--font-heading)",
fontSize: "var(--text-xl)",
fontWeight: 700,
color: "var(--forge-text)",
margin: 0,
}}
>
Build Pipeline
</h2>
<p
style={{
fontFamily: "var(--font-sans)",
fontSize: "var(--text-sm)",
color: "var(--forge-text-secondary)",
margin: "0.375rem 0 0 0",
}}
>
Compile, pack, and deploy module resources
</p>
</div>
{/* Module Section */}
<section
className="mb-6 rounded-lg border p-4"
style={{
backgroundColor: "var(--forge-surface)",
borderColor: "var(--forge-border)",
}}
>
<div className="mb-3 flex items-center justify-between">
<h3 className="text-lg font-semibold">Module</h3>
<section style={cardStyle}>
<div style={sectionHeaderStyle}>
<div style={sectionTitleStyle}>
<Hammer size={14} />
<span>Module</span>
</div>
<StatusBadge status={module.status} />
</div>
<div className="flex flex-wrap gap-2">
<div style={buttonRowStyle}>
<ActionButton
label="Compile"
variant="primary"
icon={<Play size={14} />}
disabled={isBuilding}
onClick={() => handleAction(() => api.build.compileModule(), "module")}
/>
<ActionButton
label="Pack Module"
icon={<Archive size={14} />}
disabled={isBuilding}
onClick={() => handleAction(() => api.build.packModule(), "module")}
/>
<ActionButton
label="Deploy to Server"
variant="warning"
icon={<Upload size={14} />}
disabled={isBuilding}
onClick={() => handleAction(() => api.build.deploy(), "module")}
/>
@@ -238,21 +365,19 @@ export function Build() {
</section>
{/* Haks Section */}
<section
className="mb-6 rounded-lg border p-4"
style={{
backgroundColor: "var(--forge-surface)",
borderColor: "var(--forge-border)",
}}
>
<div className="mb-3 flex items-center justify-between">
<h3 className="text-lg font-semibold">Haks</h3>
<section style={cardStyle}>
<div style={sectionHeaderStyle}>
<div style={sectionTitleStyle}>
<Package size={14} />
<span>Haks</span>
</div>
<StatusBadge status={haks.status} />
</div>
<div className="flex gap-2">
<div style={buttonRowStyle}>
<ActionButton
label="Build Haks"
variant="primary"
icon={<Play size={14} />}
disabled={isBuilding}
onClick={() => handleAction(() => api.build.buildHaks(), "haks")}
/>
@@ -265,38 +390,49 @@ export function Build() {
</section>
{/* NWNX Section */}
<section
className="rounded-lg border p-4"
style={{
backgroundColor: "var(--forge-surface)",
borderColor: "var(--forge-border)",
}}
>
<div className="mb-3 flex items-center justify-between">
<h3 className="text-lg font-semibold">
NWNX <span className="text-xs font-normal opacity-60">(Advanced)</span>
</h3>
<section style={cardStyle}>
<div style={sectionHeaderStyle}>
<div style={sectionTitleStyle}>
<Cpu size={14} />
<span>NWNX</span>
<span
style={{
fontWeight: 400,
textTransform: "none",
opacity: 0.6,
letterSpacing: "normal",
}}
>
(Advanced)
</span>
</div>
<StatusBadge status={nwnx.status} />
</div>
<div className="mb-3 flex flex-wrap gap-2">
<div style={{ ...buttonRowStyle, marginBottom: "0.75rem" }}>
<ActionButton
label="Build All"
variant="primary"
icon={<Play size={14} />}
disabled={isBuilding}
onClick={() => handleAction(() => api.build.buildNwnx(), "nwnx")}
/>
</div>
<div className="flex items-center gap-2">
<div style={{ display: "flex", alignItems: "center", gap: "0.5rem" }}>
<input
type="text"
value={nwnxTarget}
onChange={(e) => setNwnxTarget(e.target.value)}
placeholder="Target (e.g. Item, Creature)"
className="rounded border px-3 py-1.5 text-sm"
style={{
backgroundColor: "var(--forge-bg)",
borderColor: "var(--forge-border)",
border: "1px solid var(--forge-border)",
borderRadius: "0.375rem",
padding: "0.5rem 0.75rem",
fontSize: "var(--text-sm)",
color: "var(--forge-text)",
fontFamily: "var(--font-sans)",
outline: "none",
flex: "0 1 16rem",
}}
/>
<ActionButton
@@ -308,10 +444,18 @@ export function Build() {
/>
</div>
<p
className="mt-2 text-xs"
style={{ color: "#f59e0b" }}
style={{
marginTop: "0.75rem",
marginBottom: 0,
fontSize: "var(--text-xs)",
color: "var(--forge-warning)",
display: "flex",
alignItems: "center",
gap: "0.375rem",
}}
>
Requires server restart to pick up changes
<AlertTriangle size={12} />
Requires server restart to pick up changes
</p>
<BuildOutput
lines={nwnx.output}