Finalised lab page

This commit is contained in:
GeorgeWebberley 2026-02-03 11:30:13 +01:00
parent ab481053bf
commit 8369d59310
8 changed files with 154 additions and 32 deletions

79
app/forge/page.tsx Normal file
View file

@ -0,0 +1,79 @@
"use client";
import { Hammer, History, Target, Code2 } from "lucide-react";
import PageLayout from "@/components/PageLayout";
export default function ForgePage() {
return (
<PageLayout backLink="/" maxWidth="5xl">
<header className="mb-20">
<div className="flex items-center gap-3 mb-4">
<Hammer className="text-orange-500 animate-pulse" size={24} />
<h1 className="text-4xl font-bold tracking-tighter text-white uppercase">
TheForge
</h1>
</div>
<p className="text-neutral-500 max-w-2xl leading-relaxed text-sm font-mono">
<br />
The Forge is where I document my current engineering focus. This space
reflects active builds, experimental prototypes and gives a taste of
my next releases.
</p>
</header>
{/* Main Project Card */}
<section className="bg-neutral-900/40 border border-neutral-800 rounded-2xl p-8 mb-16">
<div className="flex flex-wrap items-center justify-between gap-4 mb-8">
<div>
<h2 className="text-2xl font-bold text-white mb-2 italic">
Project: PixelPals
</h2>
<div className="flex gap-4">
<span className="text-[10px] text-orange-500 font-bold uppercase tracking-[0.2em]">
Build Phase: Alpha 0.1
</span>
<span className="text-[10px] text-neutral-600 font-bold uppercase tracking-[0.2em]">
Engine: Godot / GDScript
</span>
</div>
</div>
<div className="px-4 py-2 bg-orange-500/10 border border-orange-500/20 rounded-full">
<span className="text-[10px] text-orange-500 font-bold uppercase tracking-widest">
Active Focus
</span>
</div>
</div>
<p className="text-neutral-400 text-sm leading-relaxed mb-8">
Exploring procedural generation and state-machine-driven AI behaviors.
The goal is to bridge my experience in systems architecture with
real-time game loops and interactive storytelling.
</p>
{/* Milestones / Changelog */}
<div className="space-y-6">
<h3 className="flex items-center gap-2 text-xs font-bold text-neutral-500 uppercase tracking-widest border-b border-neutral-800 pb-2">
<History size={14} /> Development_Log
</h3>
<ul className="space-y-4">
<li className="flex gap-4">
<span className="text-xs text-neutral-600 font-mono mt-1 shrink-0">
2026-02-02
</span>
<p className="text-xs text-neutral-400">
Implemented dynamic weather patterns using custom shaders.
</p>
</li>
<li className="flex gap-4">
<span className="text-xs text-neutral-600 font-mono mt-1 shrink-0">
2026-01-28
</span>
<p className="text-xs text-neutral-400">
Optimized asset loading pipeline for faster initial scene entry.
</p>
</li>
</ul>
</div>
</section>
</PageLayout>
);
}

View file

@ -127,7 +127,7 @@ export default function InfrastructurePage() {
whileTap={{ scale: 0.98 }} whileTap={{ scale: 0.98 }}
className="px-8 py-4 rounded-xl bg-white text-black font-bold text-[11px] uppercase tracking-[0.2em] flex items-center gap-3 transition-all duration-300" className="px-8 py-4 rounded-xl bg-white text-black font-bold text-[11px] uppercase tracking-[0.2em] flex items-center gap-3 transition-all duration-300"
> >
Examine Logic View case
<Zap size={14} /> <Zap size={14} />
</motion.div> </motion.div>
</Link> </Link>

View file

@ -1,6 +1,6 @@
"use client"; "use client";
import { LAB_SERVICES } from "@/data/lab"; import { LAB_SERVICES } from "@/data/lab";
import { ExternalLink, Lock, Box, Terminal, Globe } from "lucide-react"; import { Lock, Box, Globe } from "lucide-react";
import { motion } from "framer-motion"; import { motion } from "framer-motion";
import Image from "next/image"; import Image from "next/image";
import PageLayout from "@/components/PageLayout"; import PageLayout from "@/components/PageLayout";
@ -12,12 +12,12 @@ export default function LabPage() {
<div className="flex items-center gap-2 mb-4"> <div className="flex items-center gap-2 mb-4">
<Box className="text-blue-500" size={20} /> <Box className="text-blue-500" size={20} />
<h1 className="text-4xl font-bold tracking-tighter text-white uppercase"> <h1 className="text-4xl font-bold tracking-tighter text-white uppercase">
System_Lab System Lab
</h1> </h1>
</div> </div>
<p className="text-neutral-500 max-w-2xl leading-relaxed text-sm"> <p className="text-neutral-500 max-w-2xl leading-relaxed text-sm">
A registry of operational services and experimental R&D. Services A registry of self hosted operational services and experimental R&D.
labeled Services labeled
<span className="text-blue-500"> [VPN]</span> are secured via <span className="text-blue-500"> [VPN]</span> are secured via
Tailscale to maintain a hardened perimeter for sensitive telemetry. Tailscale to maintain a hardened perimeter for sensitive telemetry.
</p> </p>

View file

@ -2,7 +2,7 @@
import Link from "next/link"; import Link from "next/link";
import { motion, Variants } from "framer-motion"; import { motion, Variants } from "framer-motion";
import { Globe, Smartphone, Server, Gamepad2 } from "lucide-react"; import { Globe, Smartphone, Server, Gamepad2, Hammer } from "lucide-react";
import { useState } from "react"; import { useState } from "react";
import MonitorCard from "@/components/MonitorCard"; import MonitorCard from "@/components/MonitorCard";
import PageLayout from "@/components/PageLayout"; import PageLayout from "@/components/PageLayout";
@ -170,20 +170,28 @@ export default function Home() {
/> />
{/* Bottom Row: The Forge */} {/* Bottom Row: The Forge */}
<Link href="/forge" className="md:col-span-6">
<motion.div <motion.div
variants={itemVariants} variants={itemVariants}
className="md:col-span-6 p-8 rounded-3xl bg-neutral-900/50 border border-neutral-800 flex items-center gap-6 group hover:border-orange-500/30 transition-colors cursor-pointer" className="p-8 rounded-3xl bg-neutral-900/50 border border-neutral-800 flex items-center gap-6 group hover:border-orange-500/30 transition-colors cursor-pointer"
> >
<div className="p-4 bg-orange-500/10 rounded-2xl border border-orange-500/20 group-hover:border-orange-500/40 transition-colors"> <div className="p-4 bg-orange-500/10 rounded-2xl border border-orange-500/20 group-hover:border-orange-500/40 transition-colors">
<Gamepad2 className="text-orange-500 w-8 h-8" /> <Hammer className="text-orange-500 w-8 h-8 group-hover:rotate-12 transition-transform" />
</div> </div>
<div> <div>
<h3 className="font-bold text-xl">The Forge</h3> <div className="flex items-center gap-2 mb-0.5">
<p className="text-sm text-neutral-500"> <h3 className="font-bold text-xl tracking-tight">
Indie Game Dev & Creative Prototypes The Forge
</h3>
<span className="w-1.5 h-1.5 rounded-full bg-orange-500 animate-pulse" />
</div>
<p className="text-sm text-neutral-500 leading-tight">
A space where I demonstrate what I am currently working on and
any future projects.
</p> </p>
</div> </div>
</motion.div> </motion.div>
</Link>
</div> </div>
</motion.div> </motion.div>
</PageLayout> </PageLayout>

26
data/forge.ts Normal file
View file

@ -0,0 +1,26 @@
import { ForgeProject } from "@/types";
export const ACTIVE_BUILD: ForgeProject = {
id: "pixelpals",
name: "PixelPals",
status: "Alpha",
engine: "Godot 4.x",
description:
"A mobile-first creature collection game focusing on procedural behaviors and lightweight state-management.",
highlights: [
"State-machine AI",
"Custom Shader Pipelines",
"Mobile-optimized Loops",
],
externalLink: "/projects/pixelpals", // Link to your internal portfolio or a dedicated site
changelog: [
{
date: "2026-02-03",
update: "Refined touch-input latency for mobile devices.",
},
{
date: "2026-01-25",
update: "Integrated local SQLite database for creature persistence.",
},
],
};

View file

@ -9,7 +9,7 @@ export const LAB_SERVICES: LabService[] = [
stack: ["Next.js", "Node", "SQLite"], stack: ["Next.js", "Node", "SQLite"],
visibility: "public", visibility: "public",
url: "https://observatory.georgew.dev", url: "https://observatory.georgew.dev",
gitUrl: "https://git.georgew.dev/george/observatory", gitUrl: "https://git.georgew.dev/georgew/mission-control",
image: "/lab/observatory.jpg", image: "/lab/observatory.jpg",
uptimeId: 12, uptimeId: 12,
}, },
@ -22,6 +22,7 @@ export const LAB_SERVICES: LabService[] = [
visibility: "public", visibility: "public",
url: "https://surf.georgew.dev/d/adrx6b4/llangennith-beach-surf-data?orgId=1&from=now-24h&to=now&timezone=browser&refresh=1h&theme=dark&kiosk=true", url: "https://surf.georgew.dev/d/adrx6b4/llangennith-beach-surf-data?orgId=1&from=now-24h&to=now&timezone=browser&refresh=1h&theme=dark&kiosk=true",
image: "/lab/surf-hub.jpg", image: "/lab/surf-hub.jpg",
gitUrl: "https://git.georgew.dev/georgew/surf-hub",
uptimeId: 13, uptimeId: 13,
}, },
{ {
@ -31,8 +32,8 @@ export const LAB_SERVICES: LabService[] = [
"Dedicated audiobook server for the household. Primarily used for our Brandon Sanderson, Patrick Rothfuss, and Dungeon Crawler Carl marathons.", "Dedicated audiobook server for the household. Primarily used for our Brandon Sanderson, Patrick Rothfuss, and Dungeon Crawler Carl marathons.",
stack: ["Docker", "Compose", "Tailscale", "rclone"], stack: ["Docker", "Compose", "Tailscale", "rclone"],
visibility: "tailscale", visibility: "tailscale",
url: "https://audio.georgew.dev",
image: "/lab/audiobookshelf.jpg", image: "/lab/audiobookshelf.jpg",
gitUrl: "https://git.georgew.dev/georgew/audiobookshelf",
uptimeId: 6, uptimeId: 6,
}, },
{ {
@ -42,8 +43,8 @@ export const LAB_SERVICES: LabService[] = [
"A specialized tracker for our anime watch-lists! Features custom metadata hooks to keep our seasonal progress in sync.", "A specialized tracker for our anime watch-lists! Features custom metadata hooks to keep our seasonal progress in sync.",
stack: ["Docker", "Redis", "Tailscale"], stack: ["Docker", "Redis", "Tailscale"],
visibility: "tailscale", visibility: "tailscale",
url: "https://anime.georgew.dev",
image: "/lab/yamtrack.jpg", image: "/lab/yamtrack.jpg",
gitUrl: "https://git.georgew.dev/georgew/yamtrack",
uptimeId: 11, uptimeId: 11,
}, },
{ {
@ -53,8 +54,9 @@ export const LAB_SERVICES: LabService[] = [
"Personal document management system with OCR and automated tagging. Digitizing our physical mail and records into a searchable, versioned archive.", "Personal document management system with OCR and automated tagging. Digitizing our physical mail and records into a searchable, versioned archive.",
stack: ["Docker", "Redis", "PostgreSQL"], stack: ["Docker", "Redis", "PostgreSQL"],
visibility: "tailscale", visibility: "tailscale",
url: "https://paperless.georgew.dev",
image: "/lab/paperless.jpg", image: "/lab/paperless.jpg",
gitUrl: "https://git.georgew.dev/georgew/paperless-ngx",
uptimeId: 14, uptimeId: 14,
}, },
{ {
@ -64,8 +66,8 @@ export const LAB_SERVICES: LabService[] = [
"Automated monitoring for the essentials: NASA news updates, hobby stock alerts, and Telegram pings the second a new episode of 'The Traitors' drops.", "Automated monitoring for the essentials: NASA news updates, hobby stock alerts, and Telegram pings the second a new episode of 'The Traitors' drops.",
stack: ["Telegram API", "Webhooks"], stack: ["Telegram API", "Webhooks"],
visibility: "tailscale", visibility: "tailscale",
url: "https://alerts.georgew.dev",
image: "/lab/change-detection.jpg", image: "/lab/change-detection.jpg",
gitUrl: "https://git.georgew.dev/georgew/change-detection",
uptimeId: 15, uptimeId: 15,
}, },
{ {
@ -75,7 +77,6 @@ export const LAB_SERVICES: LabService[] = [
"The 'Engine Room.' Utilizing Portainer for orchestration, Dozzle for log streaming, and Watchtower for automated container lifecycle management across the Hetzner node.", "The 'Engine Room.' Utilizing Portainer for orchestration, Dozzle for log streaming, and Watchtower for automated container lifecycle management across the Hetzner node.",
stack: ["Portainer", "Dozzle", "Watchtower"], stack: ["Portainer", "Dozzle", "Watchtower"],
visibility: "tailscale", visibility: "tailscale",
url: "https://portainer.georgew.dev",
image: "/lab/portainer.jpg", image: "/lab/portainer.jpg",
}, },
{ {
@ -85,7 +86,6 @@ export const LAB_SERVICES: LabService[] = [
"The 'Source of Truth' for the home infrastructure. Contains deployment guides, network maps, and disaster recovery procedures for the entire node.", "The 'Source of Truth' for the home infrastructure. Contains deployment guides, network maps, and disaster recovery procedures for the entire node.",
stack: ["Wiki.js", "Markdown"], stack: ["Wiki.js", "Markdown"],
visibility: "tailscale", visibility: "tailscale",
url: "https://wiki.georgew.dev",
image: "/lab/wikijs.jpg", image: "/lab/wikijs.jpg",
uptimeId: 5, uptimeId: 5,
}, },
@ -93,11 +93,9 @@ export const LAB_SERVICES: LabService[] = [
id: "dashboard", id: "dashboard",
name: "System Dashboard", name: "System Dashboard",
description: description:
"The central entry point for the GeorgeW ecosystem. A high-level overview providing unified access to all public and VPN-secured services.", "The central entry point for the GeorgeW ecosystem, used as my personal home page. A high-level overview providing unified access to all public and VPN-secured services.",
stack: ["Next.js", "Docker", "Reverse Proxy"], stack: ["Homepage", "Docker", "Reverse Proxy"],
visibility: "public", visibility: "tailscale",
url: "https://dash.georgew.dev",
gitUrl: "https://git.georgew.dev/george/homepage",
image: "/lab/dashboard.jpg", image: "/lab/dashboard.jpg",
}, },
]; ];

BIN
public/lab/dashboard.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 250 KiB

View file

@ -47,3 +47,14 @@ export interface LabService {
image: string; image: string;
uptimeId?: number; uptimeId?: number;
} }
export interface ForgeProject {
id: string;
name: string;
status: "Alpha" | "Beta" | "R&D";
engine: string;
description: string;
highlights: string[];
externalLink?: string;
changelog: { date: string; update: string }[];
}