This commit is contained in:
@@ -62,7 +62,7 @@ function AppRoutes() {
|
||||
<p className="text-sm text-muted-foreground mb-8">Welcome to Aurora</p>
|
||||
<a
|
||||
href={`/auth/discord?return_to=${encodeURIComponent(window.location.pathname)}`}
|
||||
className="inline-flex items-center justify-center w-full rounded-md bg-primary text-primary-foreground px-4 py-2 text-sm font-medium hover:bg-primary/90 transition-colors"
|
||||
className="inline-flex items-center justify-center w-full rounded-md bg-primary text-on-primary px-4 py-2 text-sm font-label font-medium hover:opacity-90 transition-colors"
|
||||
>
|
||||
Sign in with Discord
|
||||
</a>
|
||||
|
||||
@@ -91,7 +91,7 @@ export default function Layout({
|
||||
key={path}
|
||||
onClick={() => handleNav(path)}
|
||||
className={cn(
|
||||
"w-full flex items-center gap-3 rounded-md px-3 py-2.5 text-sm font-medium transition-colors",
|
||||
"w-full flex items-center gap-3 rounded-xl px-3 py-2.5 text-sm font-medium transition-colors",
|
||||
isActive(path)
|
||||
? "bg-primary/15 text-primary border-l-4 border-primary"
|
||||
: "text-text-tertiary hover:bg-primary/8 hover:text-foreground"
|
||||
@@ -103,7 +103,7 @@ export default function Layout({
|
||||
))}
|
||||
</nav>
|
||||
|
||||
<div className="border-t border-border p-3 space-y-2">
|
||||
<div className="pt-3 p-3 space-y-2">
|
||||
{(!collapsed || mobileOpen) && (
|
||||
<div className="flex items-center gap-3 px-2 py-1.5">
|
||||
{avatarUrl ? (
|
||||
@@ -143,7 +143,7 @@ export default function Layout({
|
||||
return (
|
||||
<div className="min-h-screen flex">
|
||||
{/* Mobile header bar */}
|
||||
<div className="fixed top-0 left-0 right-0 z-40 flex items-center h-14 px-4 bg-background border-b border-border md:hidden">
|
||||
<div className="fixed top-0 left-0 right-0 z-40 flex items-center h-14 px-4 bg-background md:hidden">
|
||||
<button
|
||||
onClick={() => setMobileOpen(true)}
|
||||
className="p-2 -ml-2 rounded-md text-text-tertiary hover:text-foreground hover:bg-primary/10 transition-colors"
|
||||
@@ -164,7 +164,7 @@ export default function Layout({
|
||||
{/* Sidebar - mobile drawer + desktop fixed */}
|
||||
<aside
|
||||
className={cn(
|
||||
"fixed inset-y-0 left-0 z-50 flex flex-col bg-background border-r border-border transition-all duration-200",
|
||||
"fixed inset-y-0 left-0 z-50 flex flex-col bg-surface-container-low transition-all duration-200",
|
||||
// Mobile: off-screen drawer, shown when mobileOpen
|
||||
"w-60 -translate-x-full md:translate-x-0",
|
||||
mobileOpen && "translate-x-0",
|
||||
@@ -172,7 +172,7 @@ export default function Layout({
|
||||
!mobileOpen && collapsed && "md:w-16"
|
||||
)}
|
||||
>
|
||||
<div className="flex items-center justify-between h-14 md:h-16 px-4 border-b border-border">
|
||||
<div className="flex items-center justify-between h-14 md:h-16 px-4">
|
||||
<div className="font-display text-xl font-bold tracking-tight">
|
||||
{collapsed && !mobileOpen ? "A" : "Aurora"}
|
||||
</div>
|
||||
|
||||
@@ -56,7 +56,7 @@ export function GameLobby() {
|
||||
</div>
|
||||
<button
|
||||
onClick={() => setShowCreate(true)}
|
||||
className="rounded-md bg-primary text-primary-foreground px-4 py-2 text-sm font-medium hover:bg-primary/90 transition-colors shrink-0"
|
||||
className="rounded-xl bg-primary text-on-primary px-4 py-2 text-sm font-label font-medium hover:opacity-90 transition-colors shrink-0"
|
||||
>
|
||||
+ Create Room
|
||||
</button>
|
||||
@@ -84,21 +84,21 @@ export function GameLobby() {
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="bg-card rounded-lg border border-border">
|
||||
<div className="flex items-center gap-2 px-5 py-3 border-b border-border">
|
||||
<span className="text-sm font-semibold">Active Rooms</span>
|
||||
<span className="text-xs text-text-disabled">({activeRooms.length})</span>
|
||||
<div className="bg-card rounded-xl">
|
||||
<div className="flex items-center gap-2 px-5 py-3">
|
||||
<span className="text-sm font-display font-semibold">Active Rooms</span>
|
||||
<span className="text-xs text-text-disabled font-label">({activeRooms.length})</span>
|
||||
</div>
|
||||
{activeRooms.length === 0 ? (
|
||||
<div className="px-5 py-8 text-center text-sm text-text-tertiary">
|
||||
No active rooms. Create one to get started!
|
||||
</div>
|
||||
) : (
|
||||
<div className="divide-y divide-border">
|
||||
<div className="px-2 pb-2 space-y-0.5">
|
||||
{activeRooms.map(room => {
|
||||
const plugin = gameUIRegistry.get(room.gameSlug);
|
||||
return (
|
||||
<div key={room.id} className="flex items-center justify-between gap-3 px-4 py-3 md:px-5 hover:bg-raised/40 transition-colors">
|
||||
<div key={room.id} className="flex items-center justify-between gap-3 px-3 py-3 md:px-4 hover:bg-raised/30 transition-colors rounded-lg">
|
||||
<div className="flex items-center gap-3 min-w-0">
|
||||
<span className="text-lg shrink-0">{plugin?.icon ?? "🎮"}</span>
|
||||
<div className="min-w-0">
|
||||
@@ -120,10 +120,10 @@ export function GameLobby() {
|
||||
onClick={() => navigate(`/${room.gameSlug}/${room.id}`, {
|
||||
state: { preferAs: room.status === "waiting" ? "player" : "spectator" }
|
||||
})}
|
||||
className={`rounded-md px-3 py-1.5 text-xs font-semibold transition-colors shrink-0 ${
|
||||
className={`rounded-xl px-3 py-1.5 text-xs font-label font-semibold transition-colors shrink-0 ${
|
||||
room.status === "waiting"
|
||||
? "bg-primary text-primary-foreground hover:bg-primary/90"
|
||||
: "bg-card border border-border text-text-tertiary hover:text-foreground"
|
||||
? "bg-primary text-on-primary hover:opacity-90"
|
||||
: "bg-raised text-text-tertiary hover:text-foreground"
|
||||
}`}
|
||||
>
|
||||
{room.status === "waiting" ? "Join" : "Spectate"}
|
||||
@@ -137,14 +137,14 @@ export function GameLobby() {
|
||||
|
||||
{showCreate && (
|
||||
<div className="fixed inset-0 z-50 flex items-end sm:items-center justify-center bg-black/50" onClick={() => setShowCreate(false)}>
|
||||
<div className="bg-card border border-border rounded-t-xl sm:rounded-lg p-6 w-full sm:max-w-sm" onClick={e => e.stopPropagation()}>
|
||||
<div className="bg-surface-container-highest rounded-xl p-6 w-full sm:max-w-sm shadow-[0_20px_40px_rgba(0,0,0,0.5)]" onClick={e => e.stopPropagation()}>
|
||||
<h2 className="font-display text-base font-semibold mb-4">Create a Room</h2>
|
||||
<div className="space-y-2">
|
||||
{gameTypes.map(g => (
|
||||
<button
|
||||
key={g.slug}
|
||||
onClick={() => createRoom(g.slug)}
|
||||
className="w-full flex items-center gap-3 rounded-md border border-border px-4 py-3 text-sm font-medium hover:bg-raised/40 transition-colors"
|
||||
className="w-full flex items-center gap-3 rounded-xl bg-raised px-4 py-3 text-sm font-medium hover:bg-surface-container-high transition-colors"
|
||||
>
|
||||
<span className="text-lg">{g.icon}</span>
|
||||
<span>{g.name}</span>
|
||||
|
||||
@@ -17,7 +17,7 @@ function CopyInviteLink({ url }: { url: string }) {
|
||||
<div className="flex flex-col items-center gap-2">
|
||||
<div className="text-xs text-text-disabled mb-1">Share this link to invite:</div>
|
||||
<div className="flex items-center gap-2 w-full max-w-sm">
|
||||
<span className="flex-1 font-mono bg-surface border border-border px-2 py-1.5 rounded text-[11px] text-text-tertiary truncate">
|
||||
<span className="flex-1 font-mono bg-card rounded-lg px-2 py-1.5 text-[11px] text-text-tertiary truncate">
|
||||
{url}
|
||||
</span>
|
||||
<button
|
||||
@@ -25,7 +25,7 @@ function CopyInviteLink({ url }: { url: string }) {
|
||||
className={`shrink-0 rounded px-3 py-1.5 text-xs font-medium transition-colors ${
|
||||
copied
|
||||
? "bg-success/15 text-success"
|
||||
: "bg-card border border-border text-text-tertiary hover:text-foreground"
|
||||
: "bg-raised text-text-tertiary hover:text-foreground"
|
||||
}`}
|
||||
>
|
||||
{copied ? "Copied!" : "Copy"}
|
||||
@@ -107,14 +107,14 @@ export function GameRoom({ userId, role }: { userId: string; role?: string }) {
|
||||
</div>
|
||||
<button
|
||||
onClick={() => { leaveRoom(); navigate("/games"); }}
|
||||
className="rounded-md px-3 py-1.5 text-sm font-medium bg-card border border-border text-text-tertiary hover:text-foreground transition-colors shrink-0"
|
||||
className="rounded-md px-3 py-1.5 text-sm font-medium bg-raised text-text-tertiary hover:text-foreground transition-colors shrink-0"
|
||||
>
|
||||
Leave
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{sessionReplaced && (
|
||||
<div className="mb-4 rounded-lg border border-warning/40 bg-warning/10 px-4 py-3 flex items-center justify-between gap-3">
|
||||
<div className="mb-4 rounded-xl bg-warning/10 px-4 py-3 flex items-center justify-between gap-3">
|
||||
<p className="text-sm text-warning">
|
||||
You opened this game in another tab. Actions from this tab are disabled.
|
||||
</p>
|
||||
@@ -128,13 +128,13 @@ export function GameRoom({ userId, role }: { userId: string; role?: string }) {
|
||||
)}
|
||||
|
||||
{error && (
|
||||
<div className="mb-4 rounded-lg border border-destructive/30 bg-destructive/10 px-4 py-2 text-sm text-destructive">
|
||||
<div className="mb-4 rounded-xl bg-destructive/10 px-4 py-2 text-sm text-destructive">
|
||||
{error}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{gameOver && (
|
||||
<div className="mb-4 rounded-lg border border-primary/30 bg-primary/10 px-4 py-3">
|
||||
<div className="mb-4 rounded-xl bg-primary/10 px-4 py-3">
|
||||
<div className="text-sm font-semibold text-primary">
|
||||
{gameOver.winner
|
||||
? `Winner: ${players.find(p => p.discordId === gameOver.winner)?.username ?? gameOver.winner}`
|
||||
@@ -148,7 +148,7 @@ export function GameRoom({ userId, role }: { userId: string; role?: string }) {
|
||||
<div className="mt-4 text-center">
|
||||
<button
|
||||
onClick={() => { leaveRoom(); navigate("/games"); }}
|
||||
className="rounded-md bg-primary text-primary-foreground px-5 py-2 text-sm font-medium hover:bg-primary/90 transition-colors"
|
||||
className="rounded-xl bg-primary text-on-primary px-5 py-2 text-sm font-label font-medium hover:opacity-90 transition-colors"
|
||||
>
|
||||
Back to Lobby
|
||||
</button>
|
||||
@@ -156,7 +156,7 @@ export function GameRoom({ userId, role }: { userId: string; role?: string }) {
|
||||
)}
|
||||
|
||||
{roomStatus === "waiting" && (
|
||||
<div className="bg-card rounded-lg border border-border p-5 md:p-8">
|
||||
<div className="bg-card rounded-xl p-5 md:p-8">
|
||||
<div className="text-sm font-semibold mb-4 text-center">
|
||||
Waiting for players ({players.length}/{plugin.maxPlayers})
|
||||
</div>
|
||||
@@ -164,7 +164,7 @@ export function GameRoom({ userId, role }: { userId: string; role?: string }) {
|
||||
{Array.from({ length: plugin.maxPlayers }).map((_, i) => {
|
||||
const player = players[i];
|
||||
return (
|
||||
<div key={i} className={`flex flex-col items-center gap-2 px-4 py-3 rounded-lg border ${player ? "border-primary/40 bg-primary/5" : "border-border bg-surface"}`}>
|
||||
<div key={i} className={`flex flex-col items-center gap-2 px-4 py-3 rounded-xl ${player ? "bg-primary/10" : "bg-surface"}`}>
|
||||
<div className={`w-10 h-10 rounded-full flex items-center justify-center text-sm font-semibold ${player ? "bg-primary/20 text-primary" : "bg-surface text-text-disabled animate-pulse"}`}>
|
||||
{player ? player.username[0]?.toUpperCase() : "?"}
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Space+Grotesk:wght@600;700&family=JetBrains+Mono:wght@400;500&display=swap');
|
||||
@import url('https://fonts.googleapis.com/css2?family=Noto+Serif:ital,wght@0,400;0,600;0,700;1,400&family=Manrope:wght@400;500;600;700&family=Space+Grotesk:wght@500;600;700&family=JetBrains+Mono:wght@400;500&display=swap');
|
||||
|
||||
@import "tailwindcss";
|
||||
@plugin "tailwindcss-animate";
|
||||
@@ -6,44 +6,72 @@
|
||||
@custom-variant dark (&:is(.dark *));
|
||||
|
||||
@theme inline {
|
||||
--color-background: #0A0A0F;
|
||||
--color-foreground: #F9FAFB;
|
||||
--color-muted: #151520;
|
||||
--color-muted-foreground: #9CA3AF;
|
||||
--color-border: rgba(139, 92, 246, 0.15);
|
||||
--color-input: #1E1B4B;
|
||||
--color-ring: #8B5CF6;
|
||||
--color-primary: #8B5CF6;
|
||||
--color-primary-foreground: #FFFFFF;
|
||||
--color-secondary: #1E1B4B;
|
||||
--color-secondary-foreground: #F9FAFB;
|
||||
--color-accent: #2D2A5F;
|
||||
--color-accent-foreground: #F9FAFB;
|
||||
--color-destructive: #DC2626;
|
||||
--color-destructive-foreground: #FFFFFF;
|
||||
--color-card: #151520;
|
||||
--color-card-foreground: #F9FAFB;
|
||||
--color-success: #10B981;
|
||||
--color-warning: #F59E0B;
|
||||
--color-info: #3B82F6;
|
||||
--color-gold: #FCD34D;
|
||||
--color-surface: #1E1B4B;
|
||||
--color-raised: #2D2A5F;
|
||||
--color-text-secondary: #E5E7EB;
|
||||
--color-text-tertiary: #9CA3AF;
|
||||
--color-text-disabled: #6B7280;
|
||||
--radius-sm: 0.25rem;
|
||||
--radius-md: 0.5rem;
|
||||
--radius-lg: 0.75rem;
|
||||
/* ── Surface hierarchy: "The Void & The Light" ── */
|
||||
--color-background: #0d1323;
|
||||
--color-surface: #0d1323;
|
||||
--color-surface-container-low: #151b2c;
|
||||
--color-surface-container-high: #24293b;
|
||||
--color-surface-container-highest: #2f3446;
|
||||
|
||||
--font-display: 'Space Grotesk', 'Inter', sans-serif;
|
||||
--font-mono: 'JetBrains Mono', 'Fira Code', 'Consolas', monospace;
|
||||
/* Semantic aliases used by existing components */
|
||||
--color-card: #151b2c;
|
||||
--color-raised: #24293b;
|
||||
--color-input: #0d1323;
|
||||
--color-muted: #151b2c;
|
||||
--color-muted-foreground: #7a7f96;
|
||||
|
||||
/* ── Primary — Celestial Gold ── */
|
||||
--color-primary: #e9c349;
|
||||
--color-primary-fixed-dim: #d4af37;
|
||||
--color-primary-foreground: #1a1400;
|
||||
--color-on-primary: #1a1400;
|
||||
--color-primary-container: #3d2e00;
|
||||
--color-ring: #e9c349;
|
||||
|
||||
/* ── Secondary — Midnight Blue / Silver ── */
|
||||
--color-secondary: #1a2040;
|
||||
--color-secondary-foreground: #c8cad6;
|
||||
|
||||
/* ── Accent ── */
|
||||
--color-accent: #24293b;
|
||||
--color-accent-foreground: #e2e4f0;
|
||||
|
||||
/* ── Neutral Text ── */
|
||||
--color-foreground: #e2e4f0;
|
||||
--color-card-foreground: #e2e4f0;
|
||||
--color-text-secondary: #c8cad6;
|
||||
--color-text-tertiary: #7a7f96;
|
||||
--color-text-disabled: #4a4f66;
|
||||
|
||||
/* ── Borders: "Ghost Border" principle ── */
|
||||
--color-border: rgba(69, 70, 76, 0.18);
|
||||
--color-outline-variant: rgba(69, 70, 76, 0.45);
|
||||
|
||||
/* ── Semantic state colors ── */
|
||||
--color-destructive: #dc2626;
|
||||
--color-destructive-foreground: #ffffff;
|
||||
--color-success: #10b981;
|
||||
--color-warning: #f59e0b;
|
||||
--color-info: #3b82f6;
|
||||
--color-gold: #e9c349;
|
||||
|
||||
/* ── Roundedness Scale ── */
|
||||
--radius-sm: 0.25rem;
|
||||
--radius-md: 0.375rem;
|
||||
--radius-lg: 0.5rem;
|
||||
--radius-xl: 0.75rem;
|
||||
|
||||
/* ── Type system: "Academic Authority" ── */
|
||||
--font-display: 'Noto Serif', Georgia, 'Times New Roman', serif;
|
||||
--font-body: 'Manrope', system-ui, -apple-system, sans-serif;
|
||||
--font-label: 'Space Grotesk', system-ui, sans-serif;
|
||||
--font-mono: 'JetBrains Mono', 'Fira Code', 'Consolas', monospace;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: var(--color-background);
|
||||
color: var(--color-foreground);
|
||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||
font-family: var(--font-body);
|
||||
}
|
||||
|
||||
* {
|
||||
|
||||
@@ -308,7 +308,7 @@ function AiRemoveTab({ imageFile, imageSrc, onClear }: {
|
||||
<button
|
||||
onClick={onClear}
|
||||
className={cn(
|
||||
"flex items-center gap-1.5 px-3 py-1.5 rounded-md border border-border text-xs text-text-tertiary",
|
||||
"flex items-center gap-1.5 px-3 py-1.5 rounded-xl text-xs text-text-tertiary",
|
||||
"hover:text-destructive hover:border-destructive transition-colors",
|
||||
)}
|
||||
>
|
||||
@@ -321,8 +321,8 @@ function AiRemoveTab({ imageFile, imageSrc, onClear }: {
|
||||
className={cn(
|
||||
"flex items-center gap-1.5 px-4 py-1.5 rounded-md text-xs font-semibold transition-colors",
|
||||
status === "loading"
|
||||
? "bg-raised border border-border text-text-tertiary cursor-not-allowed"
|
||||
: "bg-primary text-white hover:bg-primary/90",
|
||||
? "bg-raised text-text-tertiary cursor-not-allowed"
|
||||
: "bg-primary text-on-primary hover:opacity-90",
|
||||
)}
|
||||
>
|
||||
{status === "loading" ? (
|
||||
@@ -334,7 +334,7 @@ function AiRemoveTab({ imageFile, imageSrc, onClear }: {
|
||||
) : (
|
||||
<button
|
||||
onClick={handleDownload}
|
||||
className="flex items-center gap-1.5 px-4 py-1.5 rounded-md text-xs font-semibold bg-primary text-white hover:bg-primary/90 transition-colors"
|
||||
className="flex items-center gap-1.5 px-4 py-1.5 rounded-md text-xs font-semibold bg-primary text-on-primary hover:opacity-90 transition-colors"
|
||||
>
|
||||
<Download className="w-3.5 h-3.5" /> Download PNG
|
||||
</button>
|
||||
@@ -351,7 +351,7 @@ function AiRemoveTab({ imageFile, imageSrc, onClear }: {
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="space-y-2">
|
||||
<p className="text-xs font-semibold text-text-tertiary uppercase tracking-wider">Original</p>
|
||||
<div className="bg-card border border-border rounded-lg overflow-hidden">
|
||||
<div className="bg-card rounded-xl overflow-hidden">
|
||||
<img src={imageSrc} className="w-full block" alt="Original" />
|
||||
</div>
|
||||
</div>
|
||||
@@ -378,7 +378,7 @@ function AiRemoveTab({ imageFile, imageSrc, onClear }: {
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="bg-card border border-border rounded-lg overflow-hidden">
|
||||
<div className="bg-card rounded-xl overflow-hidden">
|
||||
{resultUrl ? (
|
||||
<div style={BG_PRESETS[bgPreset].style}>
|
||||
<img src={resultUrl} className="w-full block" alt="Result" />
|
||||
@@ -643,7 +643,7 @@ export function BackgroundRemoval() {
|
||||
<button
|
||||
onClick={clearAll}
|
||||
className={cn(
|
||||
"flex items-center gap-1.5 px-3 py-1.5 rounded-md border border-border text-xs text-text-tertiary",
|
||||
"flex items-center gap-1.5 px-3 py-1.5 rounded-xl text-xs text-text-tertiary",
|
||||
"hover:text-destructive hover:border-destructive transition-colors",
|
||||
)}
|
||||
>
|
||||
@@ -655,8 +655,8 @@ export function BackgroundRemoval() {
|
||||
className={cn(
|
||||
"flex items-center gap-1.5 px-4 py-1.5 rounded-md text-xs font-semibold transition-colors",
|
||||
hasResult
|
||||
? "bg-primary text-white hover:bg-primary/90"
|
||||
: "bg-raised border border-border text-text-tertiary cursor-not-allowed",
|
||||
? "bg-primary text-on-primary hover:opacity-90"
|
||||
: "bg-raised text-text-tertiary cursor-not-allowed",
|
||||
)}
|
||||
>
|
||||
<Download className="w-3.5 h-3.5" /> Download PNG
|
||||
@@ -664,14 +664,14 @@ export function BackgroundRemoval() {
|
||||
</div>
|
||||
|
||||
{/* Controls */}
|
||||
<div className="bg-card border border-border rounded-xl p-4 space-y-4">
|
||||
<div className="bg-card rounded-xl p-4 space-y-4">
|
||||
{/* Row 1 — Key color + mode */}
|
||||
<div className="flex flex-wrap gap-6 items-center">
|
||||
<div className="space-y-1.5 shrink-0">
|
||||
<p className="text-xs font-medium text-text-secondary">Key Color</p>
|
||||
<div className="flex items-center gap-2">
|
||||
<div
|
||||
className="w-7 h-7 rounded-md border border-border shadow-inner shrink-0"
|
||||
className="w-7 h-7 rounded-lg shadow-inner shrink-0"
|
||||
style={
|
||||
keyColor
|
||||
? { backgroundColor: rgbToHex(...keyColor) }
|
||||
@@ -704,12 +704,12 @@ export function BackgroundRemoval() {
|
||||
|
||||
<div className="space-y-1.5 shrink-0">
|
||||
<p className="text-xs font-medium text-text-secondary">Keying Mode</p>
|
||||
<div className="flex rounded-md border border-border overflow-hidden text-xs font-medium">
|
||||
<div className="flex rounded-xl overflow-hidden text-xs font-medium">
|
||||
<button
|
||||
onClick={() => setHueMode(false)}
|
||||
className={cn(
|
||||
"px-3 py-1.5 transition-colors",
|
||||
!hueMode ? "bg-primary text-white" : "text-text-secondary hover:text-foreground hover:bg-raised",
|
||||
!hueMode ? "bg-primary text-on-primary" : "text-text-secondary hover:text-foreground hover:bg-raised",
|
||||
)}
|
||||
>
|
||||
RGB
|
||||
@@ -718,7 +718,7 @@ export function BackgroundRemoval() {
|
||||
onClick={() => setHueMode(true)}
|
||||
className={cn(
|
||||
"px-3 py-1.5 transition-colors border-l border-border",
|
||||
hueMode ? "bg-primary text-white" : "text-text-secondary hover:text-foreground hover:bg-raised",
|
||||
hueMode ? "bg-primary text-on-primary" : "text-text-secondary hover:text-foreground hover:bg-raised",
|
||||
)}
|
||||
>
|
||||
HSV
|
||||
@@ -804,7 +804,7 @@ export function BackgroundRemoval() {
|
||||
<p className="text-xs font-semibold text-text-tertiary uppercase tracking-wider">
|
||||
Original — click to pick key color
|
||||
</p>
|
||||
<div className="bg-card border border-border rounded-lg overflow-hidden">
|
||||
<div className="bg-card rounded-xl overflow-hidden">
|
||||
<canvas ref={sourceCanvasRef} className="w-full cursor-crosshair block" onClick={handleCanvasClick} title="Click a pixel to set it as the chroma key color" />
|
||||
</div>
|
||||
</div>
|
||||
@@ -831,7 +831,7 @@ export function BackgroundRemoval() {
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="bg-card border border-border rounded-lg overflow-hidden">
|
||||
<div className="bg-card rounded-xl overflow-hidden">
|
||||
<div style={BG_PRESETS[bgPreset].style} className={cn("w-full", !hasResult && "hidden")}>
|
||||
<canvas ref={glCanvasRef} className="w-full block" />
|
||||
</div>
|
||||
@@ -858,12 +858,12 @@ export function BackgroundRemoval() {
|
||||
|
||||
function ModeToggle({ mode, onChange }: { mode: Mode; onChange: (m: Mode) => void }) {
|
||||
return (
|
||||
<div className="flex rounded-md border border-border overflow-hidden text-xs font-medium shrink-0">
|
||||
<div className="flex rounded-xl overflow-hidden text-xs font-medium shrink-0">
|
||||
<button
|
||||
onClick={() => onChange("chroma")}
|
||||
className={cn(
|
||||
"px-3 py-1.5 transition-colors",
|
||||
mode === "chroma" ? "bg-primary text-white" : "text-text-secondary hover:text-foreground hover:bg-raised",
|
||||
mode === "chroma" ? "bg-primary text-on-primary" : "text-text-secondary hover:text-foreground hover:bg-raised",
|
||||
)}
|
||||
>
|
||||
Chroma Key
|
||||
@@ -872,7 +872,7 @@ function ModeToggle({ mode, onChange }: { mode: Mode; onChange: (m: Mode) => voi
|
||||
onClick={() => onChange("ai")}
|
||||
className={cn(
|
||||
"px-3 py-1.5 transition-colors border-l border-border",
|
||||
mode === "ai" ? "bg-primary text-white" : "text-text-secondary hover:text-foreground hover:bg-raised",
|
||||
mode === "ai" ? "bg-primary text-on-primary" : "text-text-secondary hover:text-foreground hover:bg-raised",
|
||||
)}
|
||||
>
|
||||
AI Remove
|
||||
|
||||
@@ -270,7 +270,7 @@ export function CanvasTool() {
|
||||
<button
|
||||
onClick={clearAll}
|
||||
className={cn(
|
||||
"flex items-center gap-1.5 px-3 py-1.5 rounded-md border border-border text-xs text-text-tertiary",
|
||||
"flex items-center gap-1.5 px-3 py-1.5 rounded-xl text-xs text-text-tertiary",
|
||||
"hover:text-destructive hover:border-destructive transition-colors",
|
||||
)}
|
||||
>
|
||||
@@ -282,8 +282,8 @@ export function CanvasTool() {
|
||||
className={cn(
|
||||
"flex items-center gap-1.5 px-4 py-1.5 rounded-md text-xs font-semibold transition-colors",
|
||||
imageReady
|
||||
? "bg-primary text-white hover:bg-primary/90"
|
||||
: "bg-raised border border-border text-text-tertiary cursor-not-allowed",
|
||||
? "bg-primary text-on-primary hover:opacity-90"
|
||||
: "bg-raised text-text-tertiary cursor-not-allowed",
|
||||
)}
|
||||
>
|
||||
<Download className="w-3.5 h-3.5" /> Download PNG
|
||||
@@ -291,7 +291,7 @@ export function CanvasTool() {
|
||||
</div>
|
||||
|
||||
{/* Controls */}
|
||||
<div className="bg-card border border-border rounded-xl p-4 space-y-4">
|
||||
<div className="bg-card rounded-xl p-4 space-y-4">
|
||||
{/* Row 1: output size */}
|
||||
<div className="flex flex-wrap gap-4 items-end">
|
||||
<div className="space-y-1.5">
|
||||
@@ -301,7 +301,7 @@ export function CanvasTool() {
|
||||
type="number" min="1" max="4096" value={outW}
|
||||
onChange={(e) => handleWChange(e.target.value)}
|
||||
className={cn(
|
||||
"w-20 bg-input border border-border rounded-md px-2 py-1.5 text-sm text-foreground font-mono",
|
||||
"w-20 bg-input border-b-2 border-outline-variant rounded-none px-2 py-1.5 text-sm text-foreground font-mono",
|
||||
"focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary/30",
|
||||
)}
|
||||
placeholder="W"
|
||||
@@ -311,7 +311,7 @@ export function CanvasTool() {
|
||||
type="number" min="1" max="4096" value={outH}
|
||||
onChange={(e) => handleHChange(e.target.value)}
|
||||
className={cn(
|
||||
"w-20 bg-input border border-border rounded-md px-2 py-1.5 text-sm text-foreground font-mono",
|
||||
"w-20 bg-input border-b-2 border-outline-variant rounded-none px-2 py-1.5 text-sm text-foreground font-mono",
|
||||
"focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary/30",
|
||||
)}
|
||||
placeholder="H"
|
||||
@@ -437,7 +437,7 @@ export function CanvasTool() {
|
||||
<p className="text-xs font-semibold text-text-tertiary uppercase tracking-wider">
|
||||
Original — {naturalW}×{naturalH}
|
||||
</p>
|
||||
<div className="bg-card border border-border rounded-lg overflow-hidden">
|
||||
<div className="bg-card rounded-xl overflow-hidden">
|
||||
<div style={CHECKERBOARD}>
|
||||
<img src={imageSrc} alt="Source" className="w-full block" />
|
||||
</div>
|
||||
@@ -467,7 +467,7 @@ export function CanvasTool() {
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="bg-card border border-border rounded-lg overflow-hidden">
|
||||
<div className="bg-card rounded-xl overflow-hidden">
|
||||
<div style={BG_PRESETS[bgPreset].style} className={cn("w-full", !imageReady && "hidden")}>
|
||||
<canvas ref={previewCanvasRef} className="w-full block" />
|
||||
</div>
|
||||
|
||||
@@ -480,7 +480,7 @@ export function CropTool() {
|
||||
<button
|
||||
onClick={clearAll}
|
||||
className={cn(
|
||||
"flex items-center gap-1.5 px-3 py-1.5 rounded-md border border-border text-xs text-text-tertiary",
|
||||
"flex items-center gap-1.5 px-3 py-1.5 rounded-xl text-xs text-text-tertiary",
|
||||
"hover:text-destructive hover:border-destructive transition-colors",
|
||||
)}
|
||||
>
|
||||
@@ -492,8 +492,8 @@ export function CropTool() {
|
||||
className={cn(
|
||||
"flex items-center gap-1.5 px-4 py-1.5 rounded-md text-xs font-semibold transition-colors",
|
||||
imageReady
|
||||
? "bg-primary text-white hover:bg-primary/90"
|
||||
: "bg-raised border border-border text-text-tertiary cursor-not-allowed",
|
||||
? "bg-primary text-on-primary hover:opacity-90"
|
||||
: "bg-raised text-text-tertiary cursor-not-allowed",
|
||||
)}
|
||||
>
|
||||
<Download className="w-3.5 h-3.5" /> Download PNG
|
||||
@@ -501,7 +501,7 @@ export function CropTool() {
|
||||
</div>
|
||||
|
||||
{/* Controls */}
|
||||
<div className="bg-card border border-border rounded-xl p-4 space-y-3">
|
||||
<div className="bg-card rounded-xl p-4 space-y-3">
|
||||
<div className="flex flex-wrap gap-4 items-end">
|
||||
<label className="space-y-1.5">
|
||||
<span className="text-xs font-medium text-text-secondary block">Output W (px)</span>
|
||||
@@ -511,7 +511,7 @@ export function CropTool() {
|
||||
value={cropW}
|
||||
onChange={(e) => setCropW(Math.max(MIN_CROP, parseIntSafe(e.target.value, MIN_CROP)))}
|
||||
className={cn(
|
||||
"w-24 bg-input border border-border rounded-md px-2 py-1.5 text-sm text-foreground font-mono",
|
||||
"w-24 bg-input border-b-2 border-outline-variant rounded-none px-2 py-1.5 text-sm text-foreground font-mono",
|
||||
"focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary/30",
|
||||
)}
|
||||
/>
|
||||
@@ -524,7 +524,7 @@ export function CropTool() {
|
||||
value={cropH}
|
||||
onChange={(e) => setCropH(Math.max(MIN_CROP, parseIntSafe(e.target.value, MIN_CROP)))}
|
||||
className={cn(
|
||||
"w-24 bg-input border border-border rounded-md px-2 py-1.5 text-sm text-foreground font-mono",
|
||||
"w-24 bg-input border-b-2 border-outline-variant rounded-none px-2 py-1.5 text-sm text-foreground font-mono",
|
||||
"focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary/30",
|
||||
)}
|
||||
/>
|
||||
@@ -536,7 +536,7 @@ export function CropTool() {
|
||||
value={imgX}
|
||||
onChange={(e) => setImgX(parseIntSafe(e.target.value, 0))}
|
||||
className={cn(
|
||||
"w-20 bg-input border border-border rounded-md px-2 py-1.5 text-sm text-foreground font-mono",
|
||||
"w-20 bg-input border-b-2 border-outline-variant rounded-none px-2 py-1.5 text-sm text-foreground font-mono",
|
||||
"focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary/30",
|
||||
)}
|
||||
/>
|
||||
@@ -548,7 +548,7 @@ export function CropTool() {
|
||||
value={imgY}
|
||||
onChange={(e) => setImgY(parseIntSafe(e.target.value, 0))}
|
||||
className={cn(
|
||||
"w-20 bg-input border border-border rounded-md px-2 py-1.5 text-sm text-foreground font-mono",
|
||||
"w-20 bg-input border-b-2 border-outline-variant rounded-none px-2 py-1.5 text-sm text-foreground font-mono",
|
||||
"focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary/30",
|
||||
)}
|
||||
/>
|
||||
@@ -567,7 +567,7 @@ export function CropTool() {
|
||||
value={padding}
|
||||
onChange={(e) => setPadding(Math.max(0, parseIntSafe(e.target.value, 0)))}
|
||||
className={cn(
|
||||
"w-20 bg-input border border-border rounded-md px-2 py-1.5 text-sm text-foreground font-mono",
|
||||
"w-20 bg-input border-b-2 border-outline-variant rounded-none px-2 py-1.5 text-sm text-foreground font-mono",
|
||||
"focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary/30",
|
||||
)}
|
||||
/>
|
||||
@@ -576,7 +576,7 @@ export function CropTool() {
|
||||
<button
|
||||
onClick={autoCenter}
|
||||
className={cn(
|
||||
"flex items-center gap-1.5 px-3 py-2 rounded-md border border-border text-xs font-medium transition-colors",
|
||||
"flex items-center gap-1.5 px-3 py-2 rounded-xl text-xs font-medium transition-colors",
|
||||
"bg-input text-text-secondary hover:text-primary hover:border-primary/60",
|
||||
)}
|
||||
title="Pan the image so non-transparent content is centered within the current output canvas"
|
||||
@@ -586,7 +586,7 @@ export function CropTool() {
|
||||
<button
|
||||
onClick={fitToContent}
|
||||
className={cn(
|
||||
"flex items-center gap-1.5 px-3 py-2 rounded-md border border-border text-xs font-medium transition-colors",
|
||||
"flex items-center gap-1.5 px-3 py-2 rounded-xl text-xs font-medium transition-colors",
|
||||
"bg-input text-text-secondary hover:text-primary hover:border-primary/60",
|
||||
)}
|
||||
title="Resize output to the non-transparent content bounding box + padding, then center"
|
||||
@@ -607,7 +607,7 @@ export function CropTool() {
|
||||
Editor
|
||||
</p>
|
||||
<div
|
||||
className="bg-card border border-border rounded-lg overflow-auto"
|
||||
className="bg-card rounded-xl overflow-auto"
|
||||
style={{ maxHeight: "62vh" }}
|
||||
>
|
||||
<canvas
|
||||
@@ -626,7 +626,7 @@ export function CropTool() {
|
||||
<p className="text-xs font-semibold text-text-tertiary uppercase tracking-wider">
|
||||
Preview
|
||||
</p>
|
||||
<div className="bg-card border border-border rounded-lg overflow-hidden">
|
||||
<div className="bg-card rounded-xl overflow-hidden">
|
||||
{imageReady ? (
|
||||
<div style={CHECKERBOARD}>
|
||||
<canvas
|
||||
|
||||
@@ -75,7 +75,7 @@ function StatCard({
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"bg-gradient-to-br from-card to-surface rounded-lg border border-border p-6 border-l-4",
|
||||
"bg-gradient-to-br from-card to-surface rounded-xl p-6 border-l-4",
|
||||
accent
|
||||
)}
|
||||
>
|
||||
@@ -105,19 +105,19 @@ function LeaderboardColumn({
|
||||
valuePrefix?: string;
|
||||
}) {
|
||||
return (
|
||||
<div className="bg-card rounded-lg border border-border">
|
||||
<div className="flex items-center gap-2 px-5 py-4 border-b border-border">
|
||||
<div className="bg-card rounded-xl">
|
||||
<div className="flex items-center gap-2 px-5 py-4">
|
||||
<Icon className="w-4 h-4 text-primary" />
|
||||
<h3 className="text-sm font-semibold">{title}</h3>
|
||||
<h3 className="text-sm font-display font-semibold">{title}</h3>
|
||||
</div>
|
||||
<div className="divide-y divide-border">
|
||||
<div className="space-y-0">
|
||||
{entries.length === 0 && (
|
||||
<div className="px-5 py-4 text-sm text-text-tertiary">No data</div>
|
||||
)}
|
||||
{entries.slice(0, 10).map((entry, i) => (
|
||||
<div
|
||||
key={entry.username}
|
||||
className="flex items-center justify-between px-5 py-3 hover:bg-raised/40 transition-colors"
|
||||
className="flex items-center justify-between px-5 py-3 hover:bg-raised/30 transition-colors rounded-lg mx-2 mb-1"
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<span
|
||||
@@ -179,7 +179,7 @@ function DashboardContent({ data }: { data: DashboardStats }) {
|
||||
<div className="space-y-8">
|
||||
{/* Maintenance banner */}
|
||||
{data.maintenanceMode && (
|
||||
<div className="flex items-center gap-3 bg-warning/10 border border-warning/30 rounded-lg px-5 py-3">
|
||||
<div className="flex items-center gap-3 bg-warning/10 rounded-xl px-5 py-3">
|
||||
<Wrench className="w-4 h-4 text-warning shrink-0" />
|
||||
<span className="text-sm text-warning font-medium">
|
||||
Maintenance mode is active
|
||||
@@ -249,13 +249,13 @@ function DashboardContent({ data }: { data: DashboardStats }) {
|
||||
{data.recentEvents.length > 0 && (
|
||||
<section>
|
||||
<h2 className="font-display text-lg font-semibold mb-4">Recent Events</h2>
|
||||
<div className="bg-card rounded-lg border border-border divide-y divide-border">
|
||||
<div className="bg-card rounded-xl">
|
||||
{data.recentEvents.slice(0, 20).map((event, i) => {
|
||||
const Icon = eventIcons[event.type];
|
||||
return (
|
||||
<div
|
||||
key={i}
|
||||
className="flex items-start gap-3 px-5 py-3 hover:bg-raised/40 transition-colors"
|
||||
className="flex items-start gap-3 px-5 py-3 hover:bg-raised/30 transition-colors rounded-lg mx-2 first:mt-2 last:mb-2"
|
||||
>
|
||||
<Icon
|
||||
className={cn("w-4 h-4 mt-0.5 shrink-0", eventColors[event.type])}
|
||||
@@ -275,7 +275,7 @@ function DashboardContent({ data }: { data: DashboardStats }) {
|
||||
)}
|
||||
|
||||
{/* Bot status footer */}
|
||||
<footer className="flex flex-wrap items-center gap-x-6 gap-y-2 text-xs text-text-tertiary border-t border-border pt-6">
|
||||
<footer className="flex flex-wrap items-center gap-x-6 gap-y-2 text-xs text-text-tertiary pt-8">
|
||||
<span className="font-medium text-text-secondary">{data.bot.name}</span>
|
||||
<span className="flex items-center gap-1.5">
|
||||
<Wifi className="w-3 h-3" />
|
||||
|
||||
@@ -370,7 +370,7 @@ export function HueShifter() {
|
||||
onClick={handleReset}
|
||||
disabled={isDefault}
|
||||
className={cn(
|
||||
"px-3 py-1.5 rounded-md border border-border text-xs text-text-tertiary transition-colors",
|
||||
"px-3 py-1.5 rounded-xl text-xs text-text-tertiary transition-colors",
|
||||
isDefault
|
||||
? "opacity-40 cursor-not-allowed"
|
||||
: "hover:text-foreground hover:border-primary/40",
|
||||
@@ -381,7 +381,7 @@ export function HueShifter() {
|
||||
<button
|
||||
onClick={clearAll}
|
||||
className={cn(
|
||||
"flex items-center gap-1.5 px-3 py-1.5 rounded-md border border-border text-xs text-text-tertiary",
|
||||
"flex items-center gap-1.5 px-3 py-1.5 rounded-xl text-xs text-text-tertiary",
|
||||
"hover:text-destructive hover:border-destructive transition-colors",
|
||||
)}
|
||||
>
|
||||
@@ -393,8 +393,8 @@ export function HueShifter() {
|
||||
className={cn(
|
||||
"flex items-center gap-1.5 px-4 py-1.5 rounded-md text-xs font-semibold transition-colors",
|
||||
imageReady && !isDefault
|
||||
? "bg-primary text-white hover:bg-primary/90"
|
||||
: "bg-raised border border-border text-text-tertiary cursor-not-allowed",
|
||||
? "bg-primary text-on-primary hover:opacity-90"
|
||||
: "bg-raised text-text-tertiary cursor-not-allowed",
|
||||
)}
|
||||
>
|
||||
<Download className="w-3.5 h-3.5" /> Download PNG
|
||||
@@ -402,7 +402,7 @@ export function HueShifter() {
|
||||
</div>
|
||||
|
||||
{/* Controls */}
|
||||
<div className="bg-card border border-border rounded-xl p-5 space-y-5">
|
||||
<div className="bg-card rounded-xl p-5 space-y-5">
|
||||
<Slider
|
||||
label="Hue Shift"
|
||||
value={hueShift}
|
||||
@@ -438,7 +438,7 @@ export function HueShifter() {
|
||||
<p className="text-xs font-semibold text-text-tertiary uppercase tracking-wider">
|
||||
Original
|
||||
</p>
|
||||
<div className="bg-card border border-border rounded-lg overflow-hidden">
|
||||
<div className="bg-card rounded-xl overflow-hidden">
|
||||
<div style={CHECKERBOARD}>
|
||||
<img src={imageSrc} alt="Original" className="w-full block" />
|
||||
</div>
|
||||
@@ -449,7 +449,7 @@ export function HueShifter() {
|
||||
<p className="text-xs font-semibold text-text-tertiary uppercase tracking-wider">
|
||||
Result
|
||||
</p>
|
||||
<div className="bg-card border border-border rounded-lg overflow-hidden">
|
||||
<div className="bg-card rounded-xl overflow-hidden">
|
||||
{/* WebGL canvas always in DOM; hidden until image is ready */}
|
||||
<div style={CHECKERBOARD}>
|
||||
<canvas
|
||||
|
||||
@@ -328,7 +328,7 @@ export function ItemStudio({
|
||||
|
||||
{/* Error */}
|
||||
{error && (
|
||||
<div className="flex items-start gap-3 p-4 bg-destructive/10 border border-destructive/30 rounded-lg">
|
||||
<div className="flex items-start gap-3 p-4 bg-destructive/10 rounded-xl">
|
||||
<AlertTriangle className="w-5 h-5 text-destructive shrink-0 mt-0.5" />
|
||||
<p className="text-sm text-destructive">{error}</p>
|
||||
</div>
|
||||
@@ -340,8 +340,8 @@ export function ItemStudio({
|
||||
onClick={handleReset}
|
||||
disabled={submitting}
|
||||
className={cn(
|
||||
"px-4 py-2 rounded-lg border border-border text-sm text-text-tertiary",
|
||||
"hover:text-foreground hover:border-primary/40 transition-colors",
|
||||
"px-4 py-2 rounded-xl bg-raised text-sm text-text-tertiary",
|
||||
"hover:text-foreground hover:bg-surface-container-highest transition-colors",
|
||||
"disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
)}
|
||||
>
|
||||
@@ -351,10 +351,10 @@ export function ItemStudio({
|
||||
onClick={handleSubmit}
|
||||
disabled={submitting || success}
|
||||
className={cn(
|
||||
"px-6 py-2 rounded-lg text-sm font-semibold transition-all flex items-center gap-2",
|
||||
"px-6 py-2 rounded-xl text-sm font-label font-semibold transition-all flex items-center gap-2",
|
||||
success
|
||||
? "bg-success text-white"
|
||||
: "bg-primary hover:bg-primary/90 text-white",
|
||||
: "bg-primary hover:opacity-90 text-on-primary",
|
||||
"disabled:opacity-70 disabled:cursor-not-allowed"
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -68,9 +68,9 @@ function SearchFilterBar({
|
||||
onChange={(e) => onSearchChange(e.target.value)}
|
||||
placeholder="Search items..."
|
||||
className={cn(
|
||||
"w-full bg-input border border-border rounded-md pl-10 pr-3 py-2 text-sm text-foreground",
|
||||
"focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary/30",
|
||||
"transition-colors"
|
||||
"w-full bg-input border-b-2 border-outline-variant rounded-none pl-10 pr-3 py-2 text-sm text-foreground",
|
||||
"focus:outline-none focus:border-primary",
|
||||
"transition-colors placeholder:text-text-disabled"
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
@@ -79,8 +79,8 @@ function SearchFilterBar({
|
||||
value={type ?? ""}
|
||||
onChange={(e) => onTypeChange(e.target.value || null)}
|
||||
className={cn(
|
||||
"bg-input border border-border rounded-md px-3 py-2 text-sm text-foreground",
|
||||
"focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary/30",
|
||||
"bg-input border-b-2 border-outline-variant rounded-none px-3 py-2 text-sm text-foreground",
|
||||
"focus:outline-none focus:border-primary",
|
||||
"transition-colors"
|
||||
)}
|
||||
>
|
||||
@@ -95,8 +95,8 @@ function SearchFilterBar({
|
||||
value={rarity ?? ""}
|
||||
onChange={(e) => onRarityChange(e.target.value || null)}
|
||||
className={cn(
|
||||
"bg-input border border-border rounded-md px-3 py-2 text-sm text-foreground",
|
||||
"focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary/30",
|
||||
"bg-input border-b-2 border-outline-variant rounded-none px-3 py-2 text-sm text-foreground",
|
||||
"focus:outline-none focus:border-primary",
|
||||
"transition-colors"
|
||||
)}
|
||||
>
|
||||
@@ -110,8 +110,8 @@ function SearchFilterBar({
|
||||
<button
|
||||
onClick={onClear}
|
||||
className={cn(
|
||||
"bg-input border border-border rounded-md px-3 py-2 text-sm text-text-secondary",
|
||||
"hover:bg-destructive hover:text-white hover:border-destructive transition-colors"
|
||||
"bg-raised rounded-xl px-3 py-2 text-sm font-label text-text-secondary",
|
||||
"hover:bg-destructive hover:text-white transition-colors"
|
||||
)}
|
||||
>
|
||||
Clear
|
||||
@@ -137,13 +137,13 @@ function ItemTable({
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="bg-card border border-border rounded-lg overflow-hidden">
|
||||
<div className="bg-card rounded-xl overflow-hidden">
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full">
|
||||
<thead className="bg-raised border-b border-border">
|
||||
<thead className="bg-raised">
|
||||
<tr>
|
||||
{columns.map((col) => (
|
||||
<th key={col} className="px-4 py-3 text-left text-xs font-semibold text-text-secondary">
|
||||
<th key={col} className="px-4 py-3 text-left text-xs font-label font-semibold text-text-secondary uppercase tracking-wide">
|
||||
{col}
|
||||
</th>
|
||||
))}
|
||||
@@ -151,7 +151,7 @@ function ItemTable({
|
||||
</thead>
|
||||
<tbody>
|
||||
{[...Array(5)].map((_, i) => (
|
||||
<tr key={i} className="border-b border-border">
|
||||
<tr key={i}>
|
||||
{columns.map((col) => (
|
||||
<td key={col} className="px-4 py-3">
|
||||
<div className="h-4 bg-raised rounded animate-pulse w-20"></div>
|
||||
@@ -168,7 +168,7 @@ function ItemTable({
|
||||
|
||||
if (items.length === 0) {
|
||||
return (
|
||||
<div className="bg-card border border-border rounded-lg p-12 text-center">
|
||||
<div className="bg-card rounded-xl p-12 text-center">
|
||||
<Package className="w-16 h-16 mx-auto mb-4 text-text-tertiary" />
|
||||
<p className="text-lg font-semibold text-text-secondary mb-2">
|
||||
No items found
|
||||
@@ -181,13 +181,13 @@ function ItemTable({
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="bg-card border border-border rounded-lg overflow-hidden">
|
||||
<div className="bg-card rounded-xl overflow-hidden">
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full">
|
||||
<thead className="bg-raised border-b border-border">
|
||||
<thead className="bg-raised">
|
||||
<tr>
|
||||
{columns.map((col) => (
|
||||
<th key={col} className="px-4 py-3 text-left text-xs font-semibold text-text-secondary">
|
||||
<th key={col} className="px-4 py-3 text-left text-xs font-label font-semibold text-text-secondary uppercase tracking-wide">
|
||||
{col}
|
||||
</th>
|
||||
))}
|
||||
@@ -197,7 +197,7 @@ function ItemTable({
|
||||
{items.map((item) => (
|
||||
<tr
|
||||
key={item.id}
|
||||
className="border-b border-border hover:bg-raised transition-colors cursor-pointer"
|
||||
className="hover:bg-raised/40 transition-colors cursor-pointer"
|
||||
onClick={() => onItemClick(item)}
|
||||
title="Click to edit"
|
||||
>
|
||||
@@ -317,10 +317,10 @@ function Pagination({
|
||||
onClick={() => onPageChange(currentPage - 1)}
|
||||
disabled={currentPage === 1}
|
||||
className={cn(
|
||||
"px-3 py-2 rounded-md text-sm font-medium transition-colors",
|
||||
"px-3 py-2 rounded-xl text-sm font-medium transition-colors",
|
||||
currentPage === 1
|
||||
? "bg-raised text-text-tertiary cursor-not-allowed"
|
||||
: "bg-input border border-border text-foreground hover:bg-raised"
|
||||
: "bg-raised text-foreground hover:bg-surface-container-highest"
|
||||
)}
|
||||
>
|
||||
<ChevronLeft className="w-4 h-4" />
|
||||
@@ -332,10 +332,10 @@ function Pagination({
|
||||
key={i}
|
||||
onClick={() => onPageChange(page)}
|
||||
className={cn(
|
||||
"px-3 py-2 rounded-md text-sm font-medium transition-colors min-w-[40px]",
|
||||
"px-3 py-2 rounded-xl text-sm font-label font-medium transition-colors min-w-[40px]",
|
||||
page === currentPage
|
||||
? "bg-primary text-white"
|
||||
: "bg-input border border-border text-foreground hover:bg-raised"
|
||||
? "bg-primary text-on-primary"
|
||||
: "bg-raised text-foreground hover:bg-surface-container-highest"
|
||||
)}
|
||||
>
|
||||
{page}
|
||||
@@ -351,10 +351,10 @@ function Pagination({
|
||||
onClick={() => onPageChange(currentPage + 1)}
|
||||
disabled={currentPage === totalPages}
|
||||
className={cn(
|
||||
"px-3 py-2 rounded-md text-sm font-medium transition-colors",
|
||||
"px-3 py-2 rounded-xl text-sm font-medium transition-colors",
|
||||
currentPage === totalPages
|
||||
? "bg-raised text-text-tertiary cursor-not-allowed"
|
||||
: "bg-input border border-border text-foreground hover:bg-raised"
|
||||
: "bg-raised text-foreground hover:bg-surface-container-highest"
|
||||
)}
|
||||
>
|
||||
<ChevronRight className="w-4 h-4" />
|
||||
@@ -364,8 +364,8 @@ function Pagination({
|
||||
value={limit}
|
||||
onChange={(e) => onLimitChange(Number(e.target.value))}
|
||||
className={cn(
|
||||
"ml-2 bg-input border border-border rounded-md px-3 py-2 text-sm text-foreground",
|
||||
"focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary/30",
|
||||
"ml-2 bg-input border-b-2 border-outline-variant rounded-none px-3 py-2 text-sm text-foreground",
|
||||
"focus:outline-none focus:border-primary",
|
||||
"transition-colors"
|
||||
)}
|
||||
>
|
||||
@@ -422,11 +422,11 @@ export default function Items() {
|
||||
return (
|
||||
<div className="flex flex-col h-full">
|
||||
{/* Header */}
|
||||
<header className="border-b border-border p-6 space-y-4">
|
||||
<h1 className="text-2xl font-bold text-foreground">Items</h1>
|
||||
<header className="p-6 space-y-4">
|
||||
<h1 className="text-2xl font-display font-bold text-foreground">Items</h1>
|
||||
|
||||
{/* Tabs */}
|
||||
<div className="flex gap-1 border-b border-border -mb-4 pb-px">
|
||||
<div className="flex gap-1 border-b border-border/30 -mb-4 pb-px">
|
||||
<button
|
||||
onClick={() => setActiveTab("all")}
|
||||
className={cn(
|
||||
@@ -504,7 +504,7 @@ export default function Items() {
|
||||
|
||||
{/* Error banner */}
|
||||
{error && (
|
||||
<div className="mx-6 mt-4 bg-destructive/10 border border-destructive/30 rounded-lg p-4 flex items-start gap-3">
|
||||
<div className="mx-6 mt-4 bg-destructive/10 rounded-xl p-4 flex items-start gap-3">
|
||||
<AlertTriangle className="w-5 h-5 text-destructive flex-shrink-0 mt-0.5" />
|
||||
<div className="flex-1">
|
||||
<p className="text-sm font-semibold text-destructive">Error</p>
|
||||
|
||||
@@ -7,7 +7,7 @@ export default function NotEnrolled() {
|
||||
You need to use the Aurora bot in Discord before you can access this panel.
|
||||
</p>
|
||||
<p className="text-xs text-text-disabled">
|
||||
Use <span className="font-mono bg-surface px-1.5 py-0.5 rounded">/enroll</span> in any server with Aurora to get started.
|
||||
Use <span className="font-mono bg-card px-1.5 py-0.5 rounded-md">/enroll</span> in any server with Aurora to get started.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -69,17 +69,17 @@ export default function PlayerDashboard({ userId }: { userId: string }) {
|
||||
<StatCard label="Items" value={String(inventory.length)} accent="success" />
|
||||
</div>
|
||||
|
||||
<div className="bg-card rounded-lg border border-border">
|
||||
<div className="flex items-center gap-2 px-5 py-3 border-b border-border">
|
||||
<span className="text-sm font-semibold">Inventory</span>
|
||||
<span className="text-xs text-text-disabled">({inventory.length})</span>
|
||||
<div className="bg-card rounded-xl">
|
||||
<div className="flex items-center gap-2 px-5 py-3">
|
||||
<span className="text-sm font-display font-semibold">Inventory</span>
|
||||
<span className="text-xs text-text-disabled font-label">({inventory.length})</span>
|
||||
</div>
|
||||
{inventory.length === 0 ? (
|
||||
<div className="px-5 py-6 text-center text-sm text-text-tertiary">No items yet</div>
|
||||
) : (
|
||||
<div className="divide-y divide-border">
|
||||
<div className="px-2 pb-2 space-y-0.5">
|
||||
{inventory.slice(0, 10).map((item, i) => (
|
||||
<div key={i} className="flex items-center justify-between px-5 py-3 hover:bg-raised/40 transition-colors">
|
||||
<div key={i} className="flex items-center justify-between px-3 py-3 hover:bg-raised/30 transition-colors rounded-lg">
|
||||
<div className="text-sm font-medium">{item.name}</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className={`text-[10px] font-semibold px-1.5 py-0.5 rounded ${rarityColor(item.rarity)}`}>
|
||||
@@ -111,7 +111,7 @@ function StatCard({ label, value, accent, subtitle }: { label: string; value: st
|
||||
success: "border-l-success",
|
||||
};
|
||||
return (
|
||||
<div className={`bg-gradient-to-br from-card to-surface border border-border rounded-lg p-5 border-l-4 ${borderColor[accent] ?? ""}`}>
|
||||
<div className={`bg-gradient-to-br from-card to-surface rounded-xl p-5 border-l-4 ${borderColor[accent] ?? ""}`}>
|
||||
<div className="text-[11px] font-semibold uppercase tracking-wider text-text-tertiary">{label}</div>
|
||||
<div className="text-2xl font-bold font-display tracking-tight mt-1">{value}</div>
|
||||
{subtitle && <div className="text-sm text-text-tertiary mt-0.5">{subtitle}</div>}
|
||||
|
||||
@@ -207,7 +207,7 @@ export default function Settings() {
|
||||
{dirty && (
|
||||
<button
|
||||
onClick={handleReset}
|
||||
className="inline-flex items-center gap-2 rounded-md border border-border px-4 py-2 text-sm font-medium text-text-secondary hover:bg-primary/10 transition-colors"
|
||||
className="inline-flex items-center gap-2 rounded-xl bg-raised px-4 py-2 text-sm font-label font-medium text-text-secondary hover:bg-surface-container-highest transition-colors"
|
||||
>
|
||||
<RotateCcw className="w-4 h-4" />
|
||||
Discard
|
||||
@@ -217,10 +217,10 @@ export default function Settings() {
|
||||
onClick={handleSave}
|
||||
disabled={!dirty || saving}
|
||||
className={cn(
|
||||
"inline-flex items-center gap-2 rounded-md px-5 py-2 text-sm font-medium transition-colors",
|
||||
"inline-flex items-center gap-2 rounded-xl px-5 py-2 text-sm font-label font-medium transition-colors",
|
||||
dirty
|
||||
? "bg-primary text-primary-foreground hover:bg-primary/90 shadow-sm shadow-primary/30"
|
||||
: "bg-primary/30 text-primary-foreground/50 cursor-not-allowed"
|
||||
? "bg-primary text-on-primary hover:opacity-90 shadow-sm shadow-primary/30"
|
||||
: "bg-primary/30 text-on-primary/50 cursor-not-allowed"
|
||||
)}
|
||||
>
|
||||
{saving ? (
|
||||
@@ -237,14 +237,14 @@ export default function Settings() {
|
||||
|
||||
{/* Error banner */}
|
||||
{error && (
|
||||
<div className="flex items-center gap-3 bg-destructive/10 border border-destructive/30 rounded-lg px-5 py-3">
|
||||
<div className="flex items-center gap-3 bg-destructive/10 rounded-xl px-5 py-3">
|
||||
<AlertTriangle className="w-4 h-4 text-destructive shrink-0" />
|
||||
<span className="text-sm text-destructive">{error}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Section tabs */}
|
||||
<div className="flex gap-1 overflow-x-auto border-b border-border pb-px">
|
||||
<div className="flex gap-1 overflow-x-auto border-b border-border/30 pb-px">
|
||||
{sections.map(({ key, label, icon: Icon }) => (
|
||||
<button
|
||||
key={key}
|
||||
@@ -324,7 +324,7 @@ export default function Settings() {
|
||||
|
||||
{/* Dirty indicator footer */}
|
||||
{dirty && (
|
||||
<div className="sticky bottom-0 -mx-6 px-6 py-3 bg-background/80 backdrop-blur border-t border-border flex items-center justify-between">
|
||||
<div className="sticky bottom-0 -mx-6 px-6 py-3 bg-surface-container-high/90 backdrop-blur flex items-center justify-between">
|
||||
<span className="text-sm text-warning font-medium">
|
||||
You have unsaved changes
|
||||
</span>
|
||||
@@ -338,7 +338,7 @@ export default function Settings() {
|
||||
<button
|
||||
onClick={handleSave}
|
||||
disabled={saving}
|
||||
className="inline-flex items-center gap-2 rounded-md bg-primary text-primary-foreground px-4 py-1.5 text-sm font-medium hover:bg-primary/90 transition-colors"
|
||||
className="inline-flex items-center gap-2 rounded-xl bg-primary text-on-primary px-4 py-1.5 text-sm font-label font-medium hover:opacity-90 transition-colors"
|
||||
>
|
||||
{saving ? (
|
||||
<Loader2 className="w-3.5 h-3.5 animate-spin" />
|
||||
|
||||
@@ -93,7 +93,7 @@ export default function Users() {
|
||||
return (
|
||||
<div className="flex flex-col h-full">
|
||||
{/* Header */}
|
||||
<header className="border-b border-border p-6 space-y-4">
|
||||
<header className="p-6 space-y-4">
|
||||
<h1 className="text-2xl font-bold text-foreground">Users</h1>
|
||||
<SearchFilterBar
|
||||
search={searchInput}
|
||||
@@ -113,7 +113,7 @@ export default function Users() {
|
||||
|
||||
{/* Error banner */}
|
||||
{error && (
|
||||
<div className="mx-6 mt-4 bg-destructive/10 border border-destructive/30 rounded-lg p-4 flex items-start gap-3">
|
||||
<div className="mx-6 mt-4 bg-destructive/10 rounded-xl p-4 flex items-start gap-3">
|
||||
<AlertTriangle className="w-5 h-5 text-destructive flex-shrink-0 mt-0.5" />
|
||||
<div className="flex-1">
|
||||
<p className="text-sm font-semibold text-destructive">Error</p>
|
||||
|
||||
@@ -38,10 +38,10 @@ export function EffectEditor({
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"border rounded-lg p-4 space-y-3",
|
||||
"rounded-xl p-4 space-y-3",
|
||||
isLootbox
|
||||
? "bg-amber-500/5 border-amber-500/25"
|
||||
: "bg-raised/20 border-border"
|
||||
? "bg-amber-500/5"
|
||||
: "bg-surface-container-high"
|
||||
)}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
@@ -57,8 +57,8 @@ export function EffectEditor({
|
||||
update({ kind: e.target.value as EffectKind, ...resetFields })
|
||||
}
|
||||
className={cn(
|
||||
"flex-1 bg-input border border-border rounded-md px-3 py-1.5 text-sm text-foreground",
|
||||
"focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary/30 transition-colors"
|
||||
"flex-1 bg-input border-b-2 border-outline-variant rounded-none px-3 py-1.5 text-sm text-foreground",
|
||||
"focus:outline-none focus:border-primary transition-colors"
|
||||
)}
|
||||
>
|
||||
{(Object.keys(EFFECT_META) as EffectKind[]).map((kind) => (
|
||||
|
||||
@@ -91,7 +91,7 @@ export function ItemPreviewCard({
|
||||
|
||||
{/* Lootbox pool mini-preview */}
|
||||
{lootboxEffect && lootboxEffect.pool.length > 0 && (
|
||||
<div className="pt-2 border-t border-border space-y-1.5">
|
||||
<div className="pt-2 mt-2 space-y-1.5">
|
||||
<div className="flex items-center gap-1.5">
|
||||
<Gift className="w-3.5 h-3.5 text-amber-400" />
|
||||
<span className="text-xs text-amber-400 font-medium">
|
||||
@@ -131,7 +131,7 @@ export function ItemPreviewCard({
|
||||
)}
|
||||
|
||||
{(draft.effects.length > 0 || draft.consume) && !lootboxEffect && (
|
||||
<div className="pt-2 border-t border-border flex flex-wrap gap-1.5">
|
||||
<div className="pt-2 mt-2 flex flex-wrap gap-1.5">
|
||||
{draft.consume && (
|
||||
<span className="inline-flex items-center gap-1 px-2 py-0.5 rounded-full bg-red-500/10 text-red-400 text-xs">
|
||||
Consumed on use
|
||||
|
||||
@@ -64,7 +64,7 @@ export function ItemSearchPicker({
|
||||
|
||||
if (value) {
|
||||
return (
|
||||
<div className="flex items-center gap-2 p-2 bg-raised/50 rounded-md border border-border">
|
||||
<div className="flex items-center gap-2 p-2 bg-raised/50 rounded-xl">
|
||||
<span
|
||||
className={cn(
|
||||
"text-xs px-1.5 py-0.5 rounded font-bold shrink-0",
|
||||
@@ -105,7 +105,7 @@ export function ItemSearchPicker({
|
||||
)}
|
||||
</div>
|
||||
{open && results.length > 0 && (
|
||||
<div className="absolute top-full left-0 right-0 z-50 mt-1 bg-card border border-border rounded-lg shadow-xl overflow-hidden">
|
||||
<div className="absolute top-full left-0 right-0 z-50 mt-1 bg-surface-container-high rounded-xl shadow-[0_20px_40px_rgba(0,0,0,0.4)] overflow-hidden">
|
||||
{results.map((item) => (
|
||||
<button
|
||||
key={item.id}
|
||||
|
||||
@@ -72,10 +72,10 @@ export function ClassificationSection({
|
||||
key={t}
|
||||
onClick={() => update({ type: t })}
|
||||
className={cn(
|
||||
"flex flex-col items-center gap-1.5 py-3 px-2 rounded-lg border text-xs font-medium transition-all",
|
||||
"flex flex-col items-center gap-1.5 py-3 px-2 rounded-xl text-xs font-label font-medium transition-all",
|
||||
active
|
||||
? "bg-primary/15 border-primary text-primary"
|
||||
: "bg-input border-border text-text-tertiary hover:border-primary/40 hover:text-foreground"
|
||||
? "bg-primary/15 text-primary"
|
||||
: "bg-raised text-text-tertiary hover:text-foreground"
|
||||
)}
|
||||
>
|
||||
<Icon className="w-4 h-4" />
|
||||
@@ -96,10 +96,10 @@ export function ClassificationSection({
|
||||
key={r}
|
||||
onClick={() => update({ rarity: r })}
|
||||
className={cn(
|
||||
"py-3 px-2 rounded-lg border text-center transition-all",
|
||||
"py-3 px-2 rounded-xl text-center transition-all border",
|
||||
active
|
||||
? cn(meta.bg, meta.text, meta.activeBorder)
|
||||
: "bg-input border-border text-text-tertiary hover:border-primary/40"
|
||||
: "bg-raised border-transparent text-text-tertiary hover:text-foreground"
|
||||
)}
|
||||
>
|
||||
<div className="text-sm font-bold">{r}</div>
|
||||
@@ -188,10 +188,10 @@ export function ArtworkSection({
|
||||
key={mode}
|
||||
onClick={() => setImageMode(mode)}
|
||||
className={cn(
|
||||
"px-3 py-1.5 rounded-md text-xs font-medium transition-colors border",
|
||||
"px-3 py-1.5 rounded-xl text-xs font-label font-medium transition-colors",
|
||||
imageMode === mode
|
||||
? "bg-primary/15 border-primary text-primary"
|
||||
: "bg-input border-border text-text-tertiary hover:text-foreground"
|
||||
? "bg-primary/15 text-primary"
|
||||
: "bg-raised text-text-tertiary hover:text-foreground"
|
||||
)}
|
||||
>
|
||||
{mode === "upload" ? "Upload File" : "Enter URL"}
|
||||
@@ -202,7 +202,7 @@ export function ArtworkSection({
|
||||
{imageMode === "upload" ? (
|
||||
<div className="space-y-2">
|
||||
{imageFile ? (
|
||||
<div className="relative rounded-lg overflow-hidden border border-border">
|
||||
<div className="relative rounded-xl overflow-hidden">
|
||||
<img
|
||||
src={imagePreview!}
|
||||
alt="Selected"
|
||||
@@ -214,7 +214,7 @@ export function ArtworkSection({
|
||||
>
|
||||
<X className="w-4 h-4" />
|
||||
</button>
|
||||
<div className="px-3 py-2 bg-card border-t border-border">
|
||||
<div className="px-3 py-2 bg-surface-container-high">
|
||||
<p className="text-xs text-text-tertiary truncate">
|
||||
{imageFile.name} ({(imageFile.size / 1024).toFixed(1)}{" "}
|
||||
KB)
|
||||
@@ -231,10 +231,10 @@ export function ArtworkSection({
|
||||
onDragLeave={() => setDragOver(false)}
|
||||
onClick={() => fileInputRef.current?.click()}
|
||||
className={cn(
|
||||
"border-2 border-dashed rounded-lg p-10 flex flex-col items-center gap-3 cursor-pointer transition-all select-none",
|
||||
"border-2 border-dashed rounded-xl p-10 flex flex-col items-center gap-3 cursor-pointer transition-all select-none",
|
||||
dragOver
|
||||
? "border-primary bg-primary/5"
|
||||
: "border-border hover:border-primary/50 hover:bg-primary/3"
|
||||
: "border-outline-variant hover:border-primary/50 hover:bg-primary/3"
|
||||
)}
|
||||
>
|
||||
<Upload
|
||||
@@ -296,7 +296,7 @@ export function ArtworkSection({
|
||||
/>
|
||||
</Field>
|
||||
{iconUrlInput && (
|
||||
<div className="flex items-center gap-3 p-3 bg-raised/40 rounded-lg border border-border">
|
||||
<div className="flex items-center gap-3 p-3 bg-raised/40 rounded-xl">
|
||||
<img
|
||||
src={iconUrlInput}
|
||||
alt="Icon preview"
|
||||
@@ -337,10 +337,10 @@ export function EffectsSection({
|
||||
aria-checked={draft.consume}
|
||||
onClick={() => update({ consume: !draft.consume })}
|
||||
className={cn(
|
||||
"relative w-10 h-5 rounded-full border transition-all shrink-0",
|
||||
"relative w-10 h-5 rounded-full transition-all shrink-0",
|
||||
draft.consume
|
||||
? "bg-primary border-primary"
|
||||
: "bg-input border-border"
|
||||
? "bg-primary"
|
||||
: "bg-raised"
|
||||
)}
|
||||
>
|
||||
<span
|
||||
@@ -367,8 +367,8 @@ export function EffectsSection({
|
||||
<button
|
||||
onClick={addEffect}
|
||||
className={cn(
|
||||
"w-full flex items-center justify-center gap-2 py-2.5 rounded-lg border border-dashed text-sm",
|
||||
"border-border text-text-tertiary hover:border-primary/50 hover:text-primary transition-colors"
|
||||
"w-full flex items-center justify-center gap-2 py-2.5 rounded-xl border border-dashed text-sm",
|
||||
"border-outline-variant text-text-tertiary hover:border-primary/50 hover:text-primary transition-colors"
|
||||
)}
|
||||
>
|
||||
<Plus className="w-4 h-4" />
|
||||
|
||||
@@ -8,8 +8,8 @@ export function SectionCard({
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<div className="bg-card border border-border rounded-xl p-5 space-y-4">
|
||||
<h3 className="text-xs font-semibold text-text-tertiary uppercase tracking-wider">
|
||||
<div className="bg-card rounded-xl p-5 space-y-4">
|
||||
<h3 className="text-xs font-label font-semibold text-text-tertiary uppercase tracking-widest">
|
||||
{title}
|
||||
</h3>
|
||||
{children}
|
||||
|
||||
@@ -298,7 +298,7 @@ export function draftFromItem(item: FullItem): Draft {
|
||||
}
|
||||
|
||||
export const inputCls = cn(
|
||||
"w-full bg-input border border-border rounded-md px-3 py-2 text-sm text-foreground",
|
||||
"focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary/30",
|
||||
"w-full bg-input border-b-2 border-outline-variant rounded-none px-3 py-2 text-sm text-foreground",
|
||||
"focus:outline-none focus:border-primary",
|
||||
"placeholder:text-text-tertiary transition-colors"
|
||||
);
|
||||
|
||||
@@ -40,7 +40,7 @@ export function LootPoolEntryEditor({
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="bg-raised/30 border border-border rounded-lg p-3 space-y-3">
|
||||
<div className="bg-surface-container-high rounded-xl p-3 space-y-3">
|
||||
{/* Header row: type tabs + weight input + percentage + remove */}
|
||||
<div className="flex items-center gap-2 flex-wrap">
|
||||
<div className="flex gap-1 flex-1 flex-wrap">
|
||||
@@ -49,10 +49,10 @@ export function LootPoolEntryEditor({
|
||||
key={t}
|
||||
onClick={() => upd({ type: t, ...resetAmounts })}
|
||||
className={cn(
|
||||
"px-2 py-1 rounded text-xs font-medium transition-colors border",
|
||||
"px-2 py-1 rounded-lg text-xs font-label font-medium transition-colors",
|
||||
entry.type === t
|
||||
? "bg-primary/15 border-primary text-primary"
|
||||
: "bg-input border-border text-text-tertiary hover:text-foreground"
|
||||
? "bg-primary/15 text-primary"
|
||||
: "bg-raised text-text-tertiary hover:text-foreground"
|
||||
)}
|
||||
>
|
||||
{LOOT_TYPE_META[t].label}
|
||||
@@ -67,7 +67,7 @@ export function LootPoolEntryEditor({
|
||||
value={entry.weight}
|
||||
onChange={(e) => upd({ weight: e.target.value })}
|
||||
className={cn(
|
||||
"w-14 bg-input border border-border rounded-md px-2 py-1 text-xs text-foreground text-center",
|
||||
"w-14 bg-input border-b-2 border-outline-variant rounded-none px-2 py-1 text-xs text-foreground text-center",
|
||||
"focus:outline-none focus:border-primary transition-colors"
|
||||
)}
|
||||
/>
|
||||
@@ -106,10 +106,10 @@ export function LootPoolEntryEditor({
|
||||
key={mode}
|
||||
onClick={() => upd({ amountMode: mode })}
|
||||
className={cn(
|
||||
"px-2.5 py-1 rounded text-xs font-medium border transition-colors",
|
||||
"px-2.5 py-1 rounded-lg text-xs font-label font-medium transition-colors",
|
||||
entry.amountMode === mode
|
||||
? "bg-primary/15 border-primary text-primary"
|
||||
: "bg-input border-border text-text-tertiary hover:text-foreground"
|
||||
? "bg-primary/15 text-primary"
|
||||
: "bg-raised text-text-tertiary hover:text-foreground"
|
||||
)}
|
||||
>
|
||||
{mode === "fixed" ? "Fixed" : "Random Range"}
|
||||
|
||||
@@ -99,8 +99,8 @@ export function LootboxEditor({
|
||||
<button
|
||||
onClick={addEntry}
|
||||
className={cn(
|
||||
"w-full flex items-center justify-center gap-2 py-2 rounded-lg border border-dashed text-xs",
|
||||
"border-border text-text-tertiary hover:border-amber-500/50 hover:text-amber-400 transition-colors"
|
||||
"w-full flex items-center justify-center gap-2 py-2 rounded-xl border border-dashed text-xs",
|
||||
"border-outline-variant text-text-tertiary hover:border-amber-500/50 hover:text-amber-400 transition-colors"
|
||||
)}
|
||||
>
|
||||
<Plus className="w-3.5 h-3.5" />
|
||||
|
||||
@@ -43,9 +43,9 @@ export function SearchFilterBar({
|
||||
onChange={(e) => onSearchChange(e.target.value)}
|
||||
placeholder="Search by username..."
|
||||
className={cn(
|
||||
"w-full bg-input border border-border rounded-md pl-10 pr-3 py-2 text-sm text-foreground",
|
||||
"focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary/30",
|
||||
"transition-colors"
|
||||
"w-full bg-input border-b-2 border-outline-variant rounded-none pl-10 pr-3 py-2 text-sm text-foreground",
|
||||
"focus:outline-none focus:border-primary",
|
||||
"transition-colors placeholder:text-text-disabled"
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
@@ -55,8 +55,8 @@ export function SearchFilterBar({
|
||||
value={classId ?? ""}
|
||||
onChange={(e) => onClassChange(e.target.value || null)}
|
||||
className={cn(
|
||||
"bg-input border border-border rounded-md px-3 py-2 text-sm text-foreground",
|
||||
"focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary/30",
|
||||
"bg-input border-b-2 border-outline-variant rounded-none px-3 py-2 text-sm text-foreground",
|
||||
"focus:outline-none focus:border-primary",
|
||||
"transition-colors"
|
||||
)}
|
||||
>
|
||||
@@ -75,8 +75,8 @@ export function SearchFilterBar({
|
||||
onActiveChange(e.target.value === "" ? null : e.target.value === "true")
|
||||
}
|
||||
className={cn(
|
||||
"bg-input border border-border rounded-md px-3 py-2 text-sm text-foreground",
|
||||
"focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary/30",
|
||||
"bg-input border-b-2 border-outline-variant rounded-none px-3 py-2 text-sm text-foreground",
|
||||
"focus:outline-none focus:border-primary",
|
||||
"transition-colors"
|
||||
)}
|
||||
>
|
||||
@@ -90,8 +90,8 @@ export function SearchFilterBar({
|
||||
value={sortBy}
|
||||
onChange={(e) => onSortByChange(e.target.value)}
|
||||
className={cn(
|
||||
"bg-input border border-border rounded-md px-3 py-2 text-sm text-foreground",
|
||||
"focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary/30",
|
||||
"bg-input border-b-2 border-outline-variant rounded-none px-3 py-2 text-sm text-foreground",
|
||||
"focus:outline-none focus:border-primary",
|
||||
"transition-colors"
|
||||
)}
|
||||
>
|
||||
@@ -105,8 +105,8 @@ export function SearchFilterBar({
|
||||
<button
|
||||
onClick={() => onSortOrderChange(sortOrder === "asc" ? "desc" : "asc")}
|
||||
className={cn(
|
||||
"bg-input border border-border rounded-md px-3 py-2 text-sm text-foreground",
|
||||
"hover:bg-raised transition-colors"
|
||||
"bg-raised rounded-xl px-3 py-2 text-sm font-label text-foreground",
|
||||
"hover:bg-surface-container-highest transition-colors"
|
||||
)}
|
||||
>
|
||||
{sortOrder === "asc" ? "\u2191 Asc" : "\u2193 Desc"}
|
||||
@@ -116,8 +116,8 @@ export function SearchFilterBar({
|
||||
<button
|
||||
onClick={onClear}
|
||||
className={cn(
|
||||
"bg-input border border-border rounded-md px-3 py-2 text-sm text-text-secondary",
|
||||
"hover:bg-destructive hover:text-white hover:border-destructive transition-colors"
|
||||
"bg-raised rounded-xl px-3 py-2 text-sm font-label text-text-secondary",
|
||||
"hover:bg-destructive hover:text-white transition-colors"
|
||||
)}
|
||||
>
|
||||
Clear
|
||||
|
||||
@@ -18,7 +18,7 @@ export function Field({
|
||||
}) {
|
||||
return (
|
||||
<div className="space-y-1.5">
|
||||
<label className="block text-xs font-semibold text-text-secondary">
|
||||
<label className="block text-xs font-label font-semibold text-text-secondary uppercase tracking-wide">
|
||||
{label}
|
||||
</label>
|
||||
{children}
|
||||
@@ -51,8 +51,8 @@ export function NumberInput({
|
||||
max={max}
|
||||
step={step}
|
||||
className={cn(
|
||||
"w-full bg-input border border-border rounded-md px-3 py-2 text-sm font-mono text-foreground",
|
||||
"focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary/30",
|
||||
"w-full bg-input border-b-2 border-outline-variant rounded-none px-3 py-2 text-sm font-mono text-foreground",
|
||||
"focus:outline-none focus:border-primary",
|
||||
"transition-colors",
|
||||
className
|
||||
)}
|
||||
@@ -78,9 +78,9 @@ export function StringInput({
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
placeholder={placeholder}
|
||||
className={cn(
|
||||
"w-full bg-input border border-border rounded-md px-3 py-2 text-sm font-mono text-foreground",
|
||||
"focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary/30",
|
||||
"transition-colors",
|
||||
"w-full bg-input border-b-2 border-outline-variant rounded-none px-3 py-2 text-sm font-mono text-foreground",
|
||||
"focus:outline-none focus:border-primary",
|
||||
"transition-colors placeholder:text-text-disabled",
|
||||
className
|
||||
)}
|
||||
/>
|
||||
@@ -105,9 +105,9 @@ export function TextArea({
|
||||
placeholder={placeholder}
|
||||
rows={rows}
|
||||
className={cn(
|
||||
"w-full bg-input border border-border rounded-md px-3 py-2 text-sm text-foreground resize-y",
|
||||
"focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary/30",
|
||||
"transition-colors"
|
||||
"w-full bg-input border-b-2 border-outline-variant rounded-none px-3 py-2 text-sm text-foreground resize-y",
|
||||
"focus:outline-none focus:border-primary",
|
||||
"transition-colors placeholder:text-text-disabled"
|
||||
)}
|
||||
/>
|
||||
);
|
||||
@@ -155,8 +155,8 @@ export function SelectInput({
|
||||
value={value}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
className={cn(
|
||||
"w-full bg-input border border-border rounded-md px-3 py-2 text-sm text-foreground",
|
||||
"focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary/30",
|
||||
"w-full bg-input border-b-2 border-outline-variant rounded-none px-3 py-2 text-sm text-foreground",
|
||||
"focus:outline-none focus:border-primary",
|
||||
"transition-colors"
|
||||
)}
|
||||
>
|
||||
@@ -186,8 +186,8 @@ export function RolePicker({
|
||||
value={value ?? ""}
|
||||
onChange={(e) => onChange(e.target.value || undefined)}
|
||||
className={cn(
|
||||
"w-full bg-input border border-border rounded-md px-3 py-2 text-sm text-foreground",
|
||||
"focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary/30",
|
||||
"w-full bg-input border-b-2 border-outline-variant rounded-none px-3 py-2 text-sm text-foreground",
|
||||
"focus:outline-none focus:border-primary",
|
||||
"transition-colors"
|
||||
)}
|
||||
>
|
||||
@@ -220,8 +220,8 @@ export function ChannelPicker({
|
||||
value={value ?? ""}
|
||||
onChange={(e) => onChange(e.target.value || undefined)}
|
||||
className={cn(
|
||||
"w-full bg-input border border-border rounded-md px-3 py-2 text-sm text-foreground",
|
||||
"focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary/30",
|
||||
"w-full bg-input border-b-2 border-outline-variant rounded-none px-3 py-2 text-sm text-foreground",
|
||||
"focus:outline-none focus:border-primary",
|
||||
"transition-colors"
|
||||
)}
|
||||
>
|
||||
@@ -257,7 +257,7 @@ export function MultiRolePicker({
|
||||
{selectedRoles.map((r) => (
|
||||
<span
|
||||
key={r.id}
|
||||
className="inline-flex items-center gap-1.5 bg-primary/15 border border-primary/30 rounded-full px-3 py-1 text-xs font-medium"
|
||||
className="inline-flex items-center gap-1.5 bg-primary/15 rounded-full px-3 py-1 text-xs font-label font-medium"
|
||||
>
|
||||
<span
|
||||
className="w-2.5 h-2.5 rounded-full"
|
||||
@@ -282,8 +282,8 @@ export function MultiRolePicker({
|
||||
if (e.target.value) onChange([...selected, e.target.value]);
|
||||
}}
|
||||
className={cn(
|
||||
"w-full bg-input border border-border rounded-md px-3 py-2 text-sm text-foreground",
|
||||
"focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary/30",
|
||||
"w-full bg-input border-b-2 border-outline-variant rounded-none px-3 py-2 text-sm text-foreground",
|
||||
"focus:outline-none focus:border-primary",
|
||||
"transition-colors"
|
||||
)}
|
||||
>
|
||||
@@ -346,7 +346,7 @@ export function CategoryPicker({
|
||||
{selectedCats.map((c) => (
|
||||
<span
|
||||
key={c.id}
|
||||
className="inline-flex items-center gap-1.5 bg-info/15 border border-info/30 rounded-full px-3 py-1 text-xs font-medium"
|
||||
className="inline-flex items-center gap-1.5 bg-info/15 rounded-full px-3 py-1 text-xs font-label font-medium"
|
||||
>
|
||||
{c.name}
|
||||
<button
|
||||
@@ -368,8 +368,8 @@ export function CategoryPicker({
|
||||
if (id) onChange([...selected, id]);
|
||||
}}
|
||||
className={cn(
|
||||
"w-full bg-input border border-border rounded-md px-3 py-2 text-sm text-foreground",
|
||||
"focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary/30",
|
||||
"w-full bg-input border-b-2 border-outline-variant rounded-none px-3 py-2 text-sm text-foreground",
|
||||
"focus:outline-none focus:border-primary",
|
||||
"transition-colors"
|
||||
)}
|
||||
>
|
||||
@@ -452,9 +452,9 @@ export function FeatureOverridesEditor({
|
||||
onClick={addFlag}
|
||||
disabled={!newKey.trim()}
|
||||
className={cn(
|
||||
"rounded-md px-4 py-2 text-sm font-medium transition-colors",
|
||||
"rounded-xl px-4 py-2 text-sm font-label font-medium transition-colors",
|
||||
newKey.trim()
|
||||
? "bg-primary/15 text-primary hover:bg-primary/25 border border-primary/30"
|
||||
? "bg-primary/15 text-primary hover:bg-primary/25"
|
||||
: "bg-raised text-text-disabled cursor-not-allowed"
|
||||
)}
|
||||
>
|
||||
@@ -475,12 +475,12 @@ export function SectionCard({
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<div className="bg-card rounded-lg border border-border">
|
||||
<div className="flex items-center gap-2.5 px-6 py-4 border-b border-border">
|
||||
<div className="bg-card rounded-xl">
|
||||
<div className="flex items-center gap-2.5 px-6 py-4">
|
||||
<Icon className="w-4 h-4 text-primary" />
|
||||
<h3 className="text-sm font-semibold">{title}</h3>
|
||||
<h3 className="text-sm font-display font-semibold">{title}</h3>
|
||||
</div>
|
||||
<div className="px-6 py-5 space-y-5">{children}</div>
|
||||
<div className="px-6 py-5 pt-0 space-y-5">{children}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -44,13 +44,13 @@ function InventoryAddForm({
|
||||
|
||||
return (
|
||||
<div className="space-y-3">
|
||||
<p className="text-xs font-semibold text-text-secondary">Add Item</p>
|
||||
<p className="text-xs font-label font-semibold text-text-secondary uppercase tracking-wide">Add Item</p>
|
||||
<select
|
||||
value={selectedItemId}
|
||||
onChange={(e) => setSelectedItemId(e.target.value)}
|
||||
className={cn(
|
||||
"w-full bg-input border border-border rounded-md px-3 py-2 text-sm text-foreground",
|
||||
"focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary/30",
|
||||
"w-full bg-input border-b-2 border-outline-variant rounded-none px-3 py-2 text-sm text-foreground",
|
||||
"focus:outline-none focus:border-primary",
|
||||
"transition-colors"
|
||||
)}
|
||||
>
|
||||
@@ -69,8 +69,8 @@ function InventoryAddForm({
|
||||
min="1"
|
||||
placeholder="Qty"
|
||||
className={cn(
|
||||
"w-20 bg-input border border-border rounded-md px-3 py-2 text-sm font-mono text-foreground",
|
||||
"focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary/30",
|
||||
"w-20 bg-input border-b-2 border-outline-variant rounded-none px-3 py-2 text-sm font-mono text-foreground",
|
||||
"focus:outline-none focus:border-primary",
|
||||
"transition-colors"
|
||||
)}
|
||||
/>
|
||||
@@ -78,8 +78,8 @@ function InventoryAddForm({
|
||||
onClick={handleAdd}
|
||||
disabled={!selectedItemId}
|
||||
className={cn(
|
||||
"flex-1 px-3 py-2 rounded-md text-sm font-medium transition-colors",
|
||||
"bg-primary text-white hover:bg-primary/90",
|
||||
"flex-1 px-3 py-2 rounded-xl text-sm font-label font-medium transition-colors",
|
||||
"bg-primary text-on-primary hover:opacity-90",
|
||||
"disabled:opacity-50 disabled:cursor-not-allowed",
|
||||
"flex items-center justify-center gap-1.5"
|
||||
)}
|
||||
@@ -135,12 +135,12 @@ export function UserDetailPanel({
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 md:relative md:w-96 border-l border-border bg-card overflow-auto z-50 md:z-auto">
|
||||
<div className="fixed inset-0 md:relative md:w-96 bg-surface-container-low overflow-auto z-50 md:z-auto">
|
||||
<div className="p-6 space-y-6 pb-24">
|
||||
{/* Header */}
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="flex-1">
|
||||
<h2 className="text-lg font-semibold text-foreground mb-1">
|
||||
<h2 className="text-lg font-display font-semibold text-foreground mb-1">
|
||||
{user.username}
|
||||
</h2>
|
||||
<p className="text-xs font-mono text-text-tertiary">{user.id}</p>
|
||||
@@ -226,7 +226,7 @@ export function UserDetailPanel({
|
||||
{inventoryDraft.map((entry) => (
|
||||
<div
|
||||
key={entry.itemId}
|
||||
className="flex items-center justify-between gap-3 p-2 bg-raised rounded-md"
|
||||
className="flex items-center justify-between gap-3 p-2 bg-raised rounded-xl"
|
||||
>
|
||||
<div className="flex-1 min-w-0">
|
||||
<p className="text-sm font-medium text-foreground truncate">
|
||||
@@ -238,7 +238,7 @@ export function UserDetailPanel({
|
||||
</div>
|
||||
<button
|
||||
onClick={() => onRemoveItem(entry.itemId)}
|
||||
className="p-1.5 text-text-tertiary hover:text-destructive hover:bg-destructive/10 rounded transition-colors"
|
||||
className="p-1.5 text-text-tertiary hover:text-destructive hover:bg-destructive/10 rounded-lg transition-colors"
|
||||
title="Remove item"
|
||||
>
|
||||
<Trash2 className="w-4 h-4" />
|
||||
@@ -249,7 +249,7 @@ export function UserDetailPanel({
|
||||
)}
|
||||
|
||||
{/* Add Item Form */}
|
||||
<div className="mt-4 pt-4 border-t border-border">
|
||||
<div className="mt-4 pt-4">
|
||||
<InventoryAddForm items={items} onAdd={onAddItem} />
|
||||
</div>
|
||||
</SectionCard>
|
||||
@@ -257,7 +257,7 @@ export function UserDetailPanel({
|
||||
|
||||
{/* Sticky footer for save/discard (only shown when dirty) */}
|
||||
{isDirty && (
|
||||
<div className="sticky bottom-0 left-0 right-0 border-t border-border bg-card p-4 space-y-3">
|
||||
<div className="sticky bottom-0 left-0 right-0 bg-surface-container-high p-4 space-y-3">
|
||||
<div className="flex items-center gap-2 text-amber-400">
|
||||
<AlertTriangle className="w-4 h-4" />
|
||||
<span className="text-sm font-medium">You have unsaved changes</span>
|
||||
@@ -267,8 +267,8 @@ export function UserDetailPanel({
|
||||
onClick={onDiscard}
|
||||
disabled={saving}
|
||||
className={cn(
|
||||
"flex-1 px-4 py-2 rounded-md text-sm font-medium transition-colors",
|
||||
"bg-input border border-border text-foreground hover:bg-raised",
|
||||
"flex-1 px-4 py-2 rounded-xl text-sm font-label font-medium transition-colors",
|
||||
"bg-raised text-foreground hover:bg-surface-container-highest",
|
||||
saving && "opacity-50 cursor-not-allowed"
|
||||
)}
|
||||
>
|
||||
@@ -278,8 +278,8 @@ export function UserDetailPanel({
|
||||
onClick={onSave}
|
||||
disabled={saving}
|
||||
className={cn(
|
||||
"flex-1 px-4 py-2 rounded-md text-sm font-medium transition-colors",
|
||||
"bg-primary text-white hover:bg-primary/90",
|
||||
"flex-1 px-4 py-2 rounded-xl text-sm font-label font-medium transition-colors",
|
||||
"bg-primary text-on-primary hover:opacity-90",
|
||||
"flex items-center justify-center gap-2",
|
||||
saving && "opacity-50 cursor-not-allowed"
|
||||
)}
|
||||
|
||||
@@ -15,7 +15,7 @@ export function Field({
|
||||
}) {
|
||||
return (
|
||||
<div className="space-y-1.5">
|
||||
<label className="block text-xs font-semibold text-text-secondary">
|
||||
<label className="block text-xs font-label font-semibold text-text-secondary uppercase tracking-wide">
|
||||
{label}
|
||||
</label>
|
||||
{children}
|
||||
@@ -48,8 +48,8 @@ export function NumberInput({
|
||||
max={max}
|
||||
step={step}
|
||||
className={cn(
|
||||
"w-full bg-input border border-border rounded-md px-3 py-2 text-sm font-mono text-foreground",
|
||||
"focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary/30",
|
||||
"w-full bg-input border-b-2 border-outline-variant rounded-none px-3 py-2 text-sm font-mono text-foreground",
|
||||
"focus:outline-none focus:border-primary",
|
||||
"transition-colors",
|
||||
className
|
||||
)}
|
||||
@@ -75,9 +75,9 @@ export function StringInput({
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
placeholder={placeholder}
|
||||
className={cn(
|
||||
"w-full bg-input border border-border rounded-md px-3 py-2 text-sm font-mono text-foreground",
|
||||
"focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary/30",
|
||||
"transition-colors",
|
||||
"w-full bg-input border-b-2 border-outline-variant rounded-none px-3 py-2 text-sm font-mono text-foreground",
|
||||
"focus:outline-none focus:border-primary",
|
||||
"transition-colors placeholder:text-text-disabled",
|
||||
className
|
||||
)}
|
||||
/>
|
||||
@@ -126,8 +126,8 @@ export function SelectInput({
|
||||
value={value}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
className={cn(
|
||||
"w-full bg-input border border-border rounded-md px-3 py-2 text-sm text-foreground",
|
||||
"focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary/30",
|
||||
"w-full bg-input border-b-2 border-outline-variant rounded-none px-3 py-2 text-sm text-foreground",
|
||||
"focus:outline-none focus:border-primary",
|
||||
"transition-colors"
|
||||
)}
|
||||
>
|
||||
@@ -150,10 +150,10 @@ export function SectionCard({
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<div className="bg-card border border-border rounded-lg p-4">
|
||||
<div className="bg-card rounded-xl p-4">
|
||||
<div className="flex items-center gap-2 mb-4">
|
||||
<Icon className="w-4 h-4 text-primary" />
|
||||
<h3 className="text-sm font-semibold text-foreground">{title}</h3>
|
||||
<h3 className="text-sm font-display font-semibold text-foreground">{title}</h3>
|
||||
</div>
|
||||
{children}
|
||||
</div>
|
||||
|
||||
@@ -57,7 +57,7 @@ export function UserPagination({
|
||||
return (
|
||||
<div className="mt-4 flex flex-wrap gap-4 items-center justify-between">
|
||||
{/* Items info */}
|
||||
<p className="text-sm text-text-secondary">
|
||||
<p className="text-sm font-label text-text-secondary">
|
||||
Showing {startItem}\u2013{endItem} of {formatNumber(total)} users
|
||||
</p>
|
||||
|
||||
@@ -68,10 +68,10 @@ export function UserPagination({
|
||||
onClick={() => onPageChange(currentPage - 1)}
|
||||
disabled={currentPage === 1}
|
||||
className={cn(
|
||||
"px-3 py-2 rounded-md text-sm font-medium transition-colors",
|
||||
"px-3 py-2 rounded-xl text-sm font-medium transition-colors",
|
||||
currentPage === 1
|
||||
? "bg-raised text-text-tertiary cursor-not-allowed"
|
||||
: "bg-input border border-border text-foreground hover:bg-raised"
|
||||
: "bg-raised text-foreground hover:bg-surface-container-highest"
|
||||
)}
|
||||
>
|
||||
<ChevronLeft className="w-4 h-4" />
|
||||
@@ -84,10 +84,10 @@ export function UserPagination({
|
||||
key={i}
|
||||
onClick={() => onPageChange(page)}
|
||||
className={cn(
|
||||
"px-3 py-2 rounded-md text-sm font-medium transition-colors min-w-[40px]",
|
||||
"px-3 py-2 rounded-xl text-sm font-label font-medium transition-colors min-w-[40px]",
|
||||
page === currentPage
|
||||
? "bg-primary text-white"
|
||||
: "bg-input border border-border text-foreground hover:bg-raised"
|
||||
? "bg-primary text-on-primary"
|
||||
: "bg-raised text-foreground hover:bg-surface-container-highest"
|
||||
)}
|
||||
>
|
||||
{page}
|
||||
@@ -104,10 +104,10 @@ export function UserPagination({
|
||||
onClick={() => onPageChange(currentPage + 1)}
|
||||
disabled={currentPage === totalPages}
|
||||
className={cn(
|
||||
"px-3 py-2 rounded-md text-sm font-medium transition-colors",
|
||||
"px-3 py-2 rounded-xl text-sm font-medium transition-colors",
|
||||
currentPage === totalPages
|
||||
? "bg-raised text-text-tertiary cursor-not-allowed"
|
||||
: "bg-input border border-border text-foreground hover:bg-raised"
|
||||
: "bg-raised text-foreground hover:bg-surface-container-highest"
|
||||
)}
|
||||
>
|
||||
<ChevronRight className="w-4 h-4" />
|
||||
@@ -118,8 +118,8 @@ export function UserPagination({
|
||||
value={limit}
|
||||
onChange={(e) => onLimitChange(Number(e.target.value))}
|
||||
className={cn(
|
||||
"ml-2 bg-input border border-border rounded-md px-3 py-2 text-sm text-foreground",
|
||||
"focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary/30",
|
||||
"ml-2 bg-input border-b-2 border-outline-variant rounded-none px-3 py-2 text-sm text-foreground",
|
||||
"focus:outline-none focus:border-primary",
|
||||
"transition-colors"
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -18,31 +18,31 @@ export function UserTable({
|
||||
}) {
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="bg-card border border-border rounded-lg overflow-hidden">
|
||||
<div className="bg-card rounded-xl overflow-hidden">
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full">
|
||||
<thead className="bg-raised border-b border-border">
|
||||
<thead className="bg-raised">
|
||||
<tr>
|
||||
<th className="px-4 py-3 text-left text-xs font-semibold text-text-secondary">
|
||||
<th className="px-4 py-3 text-left text-xs font-label font-semibold text-text-secondary uppercase tracking-wide">
|
||||
Username
|
||||
</th>
|
||||
<th className="px-4 py-3 text-left text-xs font-semibold text-text-secondary">
|
||||
<th className="px-4 py-3 text-left text-xs font-label font-semibold text-text-secondary uppercase tracking-wide">
|
||||
Level
|
||||
</th>
|
||||
<th className="px-4 py-3 text-left text-xs font-semibold text-text-secondary">
|
||||
<th className="px-4 py-3 text-left text-xs font-label font-semibold text-text-secondary uppercase tracking-wide">
|
||||
Balance
|
||||
</th>
|
||||
<th className="px-4 py-3 text-left text-xs font-semibold text-text-secondary">
|
||||
<th className="px-4 py-3 text-left text-xs font-label font-semibold text-text-secondary uppercase tracking-wide">
|
||||
Class
|
||||
</th>
|
||||
<th className="px-4 py-3 text-left text-xs font-semibold text-text-secondary">
|
||||
<th className="px-4 py-3 text-left text-xs font-label font-semibold text-text-secondary uppercase tracking-wide">
|
||||
Status
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{[...Array(5)].map((_, i) => (
|
||||
<tr key={i} className="border-b border-border">
|
||||
<tr key={i}>
|
||||
<td className="px-4 py-3">
|
||||
<div className="h-4 bg-raised rounded animate-pulse w-32"></div>
|
||||
</td>
|
||||
@@ -69,9 +69,9 @@ export function UserTable({
|
||||
|
||||
if (users.length === 0) {
|
||||
return (
|
||||
<div className="bg-card border border-border rounded-lg p-12 text-center">
|
||||
<div className="bg-card rounded-xl p-12 text-center">
|
||||
<UserCircle2 className="w-16 h-16 mx-auto mb-4 text-text-tertiary" />
|
||||
<p className="text-lg font-semibold text-text-secondary mb-2">
|
||||
<p className="text-lg font-display font-semibold text-text-secondary mb-2">
|
||||
No users found
|
||||
</p>
|
||||
<p className="text-sm text-text-tertiary">
|
||||
@@ -82,27 +82,27 @@ export function UserTable({
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="bg-card border border-border rounded-lg overflow-hidden">
|
||||
<div className="bg-card rounded-xl overflow-hidden">
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full">
|
||||
<thead className="bg-raised border-b border-border">
|
||||
<thead className="bg-raised">
|
||||
<tr>
|
||||
<th className="px-4 py-3 text-left text-xs font-semibold text-text-secondary">
|
||||
<th className="px-4 py-3 text-left text-xs font-label font-semibold text-text-secondary uppercase tracking-wide">
|
||||
Username
|
||||
</th>
|
||||
<th className="px-4 py-3 text-left text-xs font-semibold text-text-secondary">
|
||||
<th className="px-4 py-3 text-left text-xs font-label font-semibold text-text-secondary uppercase tracking-wide">
|
||||
Level
|
||||
</th>
|
||||
<th className="px-4 py-3 text-left text-xs font-semibold text-text-secondary">
|
||||
<th className="px-4 py-3 text-left text-xs font-label font-semibold text-text-secondary uppercase tracking-wide">
|
||||
Balance
|
||||
</th>
|
||||
<th className="px-4 py-3 text-left text-xs font-semibold text-text-secondary">
|
||||
<th className="px-4 py-3 text-left text-xs font-label font-semibold text-text-secondary uppercase tracking-wide">
|
||||
XP
|
||||
</th>
|
||||
<th className="px-4 py-3 text-left text-xs font-semibold text-text-secondary">
|
||||
<th className="px-4 py-3 text-left text-xs font-label font-semibold text-text-secondary uppercase tracking-wide">
|
||||
Class
|
||||
</th>
|
||||
<th className="px-4 py-3 text-left text-xs font-semibold text-text-secondary">
|
||||
<th className="px-4 py-3 text-left text-xs font-label font-semibold text-text-secondary uppercase tracking-wide">
|
||||
Status
|
||||
</th>
|
||||
</tr>
|
||||
@@ -112,7 +112,7 @@ export function UserTable({
|
||||
<tr
|
||||
key={user.id}
|
||||
onClick={() => onSelectUser(user)}
|
||||
className="border-b border-border hover:bg-raised cursor-pointer transition-colors"
|
||||
className="hover:bg-raised/40 cursor-pointer transition-colors"
|
||||
>
|
||||
<td className="px-4 py-3">
|
||||
<div className="flex items-center gap-3">
|
||||
@@ -152,7 +152,7 @@ export function UserTable({
|
||||
<td className="px-4 py-3">
|
||||
<span
|
||||
className={cn(
|
||||
"inline-flex items-center px-2 py-1 rounded-full text-xs font-medium",
|
||||
"inline-flex items-center px-2 py-1 rounded-full text-xs font-label font-medium",
|
||||
user.isActive
|
||||
? "bg-green-500/20 text-green-400"
|
||||
: "bg-gray-500/20 text-gray-400"
|
||||
|
||||
Reference in New Issue
Block a user