From 6c91f7fef04d858b8a552d968ced3ab51357fb7c Mon Sep 17 00:00:00 2001 From: hurkicorgi Date: Mon, 13 Apr 2026 04:58:25 -0600 Subject: [PATCH] UX/UI/perf pass: admin tabs, theme toggle, log polish, mod search, JAR cache - Admin page split into tabs (Server/Players/Chat/Mods/Backups/Logs) with hash + localStorage persistence; inactive tabs no longer mount. - Log viewer: level color-coding, search, level filter chips, auto-scroll toggle, copy button, visible-line count. - Installed mods list: search field + side filter (all/both/server/client) with live count; public ModList gets skeleton + empty states and search. - Theme toggle with no-flash inline init, localStorage + system preference. - Layout: full OG / Twitter metadata, title template, keywords, dual-theme themeColor, metadataBase. - lib/mods.ts: per-jar mtime+size parse cache (cold 6s -> warm ~45ms on /api/mods for the full mod list); cache eviction on mod removal. - ChatBridge polling eased 3s -> 5s with 2s stale window. Co-Authored-By: Claude Opus 4.6 (1M context) --- app/admin/page.tsx | 37 +++------ app/layout.tsx | 54 +++++++++++-- components/AdminTabs.tsx | 80 ++++++++++++++++++++ components/ChatBridge.tsx | 3 +- components/LogViewer.tsx | 151 ++++++++++++++++++++++++++++++++----- components/ModList.tsx | 102 ++++++++++++++++++------- components/ModManager.tsx | 56 +++++++++++++- components/Navbar.tsx | 2 + components/ThemeToggle.tsx | 53 +++++++++++++ lib/mods.ts | 41 ++++++++-- 10 files changed, 490 insertions(+), 89 deletions(-) create mode 100644 components/AdminTabs.tsx create mode 100644 components/ThemeToggle.tsx diff --git a/app/admin/page.tsx b/app/admin/page.tsx index e6656d9..c491749 100644 --- a/app/admin/page.tsx +++ b/app/admin/page.tsx @@ -1,14 +1,7 @@ import { auth } from "@/lib/auth"; import { redirect } from "next/navigation"; import { Navbar } from "@/components/Navbar"; -import { ClientOnly } from "@/components/ClientOnly"; -import { ServerControls } from "@/components/ServerControls"; -import { Analytics } from "@/components/Analytics"; -import { PlayerManager } from "@/components/PlayerManager"; -import { ChatBridge } from "@/components/ChatBridge"; -import { ModManager } from "@/components/ModManager"; -import { BackupManager } from "@/components/BackupManager"; -import { LogViewer } from "@/components/LogViewer"; +import { AdminTabs } from "@/components/AdminTabs"; import Link from "next/link"; export default async function AdminPage() { @@ -28,26 +21,18 @@ export default async function AdminPage() { - -
- - - - - - - +
+ -
- - Back to dashboard - -
+
+ + Back to dashboard +
- +
); } diff --git a/app/layout.tsx b/app/layout.tsx index 561ff35..2f3dcde 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -13,28 +13,68 @@ const geistMono = Geist_Mono({ subsets: ["latin"], }); +const SITE_URL = "https://minecraft.hurkicorgi.com"; +const SITE_TITLE = "HurkiCorgi MC"; +const SITE_DESCRIPTION = + "Modded Minecraft Forge 1.20.1 server — Create & Engineering, Raids, Survival. Live status, mod list, and installer."; + export const metadata: Metadata = { - title: "HurkiCorgi MC", - description: "Create & Engineering | Raids | Survival - Minecraft Server", + metadataBase: new URL(SITE_URL), + title: { + default: SITE_TITLE, + template: `%s · ${SITE_TITLE}`, + }, + description: SITE_DESCRIPTION, manifest: "/manifest.json", + applicationName: SITE_TITLE, + keywords: ["Minecraft", "Forge", "Create mod", "modded server", "HurkiCorgi"], appleWebApp: { capable: true, statusBarStyle: "black-translucent", - title: "HurkiCorgi MC", + title: SITE_TITLE, }, icons: { icon: "/icon.svg", apple: "/icon.svg", }, + openGraph: { + type: "website", + url: SITE_URL, + title: SITE_TITLE, + description: SITE_DESCRIPTION, + siteName: SITE_TITLE, + images: [{ url: "/icon.svg", width: 512, height: 512, alt: SITE_TITLE }], + }, + twitter: { + card: "summary", + title: SITE_TITLE, + description: SITE_DESCRIPTION, + images: ["/icon.svg"], + }, + robots: { index: true, follow: true }, }; export const viewport: Viewport = { width: "device-width", initialScale: 1, maximumScale: 1, - themeColor: "#1a1a2e", + themeColor: [ + { media: "(prefers-color-scheme: dark)", color: "#1a1a2e" }, + { media: "(prefers-color-scheme: light)", color: "#f8fafc" }, + ], }; +const themeInit = ` +try { + var s = localStorage.getItem('theme'); + var m = window.matchMedia('(prefers-color-scheme: light)').matches; + var t = s || (m ? 'light' : 'dark'); + var h = document.documentElement; + h.classList.add(t); + h.style.colorScheme = t; +} catch (e) { document.documentElement.classList.add('dark'); } +`; + export default function RootLayout({ children, }: Readonly<{ @@ -43,8 +83,12 @@ export default function RootLayout({ return ( + +