Compare commits
5 Commits
5dea6121a3
...
2026.04.3
| Author | SHA1 | Date | |
|---|---|---|---|
| d34f9f136c | |||
| 22660bed7a | |||
| 7f614d28b5 | |||
| e9d7a994c7 | |||
| f6118aa7a4 |
+14
-58
@@ -1,68 +1,24 @@
|
||||
import type { Metadata } from "next";
|
||||
import Widget from "@/components/Widget";
|
||||
import { archiveProjects } from "@/data/projects";
|
||||
"use client";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Archive | Tyler Koenig",
|
||||
description: "Earlier projects and experiments — browser extensions, canvas apps, and bootcamp work.",
|
||||
};
|
||||
import { useEffect } from "react";
|
||||
|
||||
export default function ArchiveRedirect() {
|
||||
useEffect(() => {
|
||||
window.location.replace("/projects/");
|
||||
}, []);
|
||||
|
||||
export default function ArchivePage() {
|
||||
return (
|
||||
<>
|
||||
<div className="mb-4lh">
|
||||
<p className="font-mono text-sm font-bold text-[var(--color-text)] mb-1lh">
|
||||
<span className="text-[var(--color-accent-green)] select-none mr-1ch" aria-hidden="true">❯</span>
|
||||
tyler/projects/archive
|
||||
</p>
|
||||
<p className="font-mono text-sm text-[var(--color-text)] leading-relaxed max-w-xl opacity-80">
|
||||
Experiments, browser extensions, and bootcamp projects. Kept here for context — not
|
||||
representative of current work.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<Widget title="tyler/projects/archive" badge={archiveProjects.length} as="section">
|
||||
<div className="flex flex-col gap-px bg-[var(--color-border)]">
|
||||
{archiveProjects.map((project) => (
|
||||
<meta httpEquiv="refresh" content="0; url=/projects/" />
|
||||
<p className="font-mono text-sm text-[var(--color-text)]">
|
||||
This page moved.{" "}
|
||||
<a
|
||||
key={project.slug}
|
||||
href={project.githubUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="bg-[var(--color-surface)] hover:bg-[var(--color-surface-raised)] flex items-start justify-between gap-2ch px-2ch py-1lh group"
|
||||
href="/projects/"
|
||||
className="text-[var(--color-accent-green)] underline"
|
||||
>
|
||||
<div className="flex flex-col gap-1ch flex-1 min-w-0">
|
||||
<div className="flex items-center gap-1ch">
|
||||
{project.year && (
|
||||
<span className="font-mono text-sm text-[var(--color-text-dim)] shrink-0">
|
||||
{project.year}
|
||||
</span>
|
||||
)}
|
||||
<span className="font-mono text-sm text-[var(--color-text)] group-hover:text-[var(--color-accent-green)] truncate">
|
||||
{project.title}
|
||||
</span>
|
||||
</div>
|
||||
<p className="font-mono text-sm text-[var(--color-text)] leading-relaxed opacity-75">
|
||||
{project.description}
|
||||
</p>
|
||||
<div className="flex flex-wrap gap-x-1ch gap-y-0.5">
|
||||
{project.tags.map((tag) => (
|
||||
<span key={tag} className="font-mono text-sm text-[var(--color-text-dim)]">
|
||||
{tag}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<span
|
||||
className="font-mono text-sm text-[var(--color-text-label)] group-hover:text-[var(--color-text)] shrink-0 mt-0.5"
|
||||
aria-hidden="true"
|
||||
>
|
||||
↗
|
||||
</span>
|
||||
/projects/
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</Widget>
|
||||
</p>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -155,7 +155,7 @@ export default function HomelabPage() {
|
||||
{/* VLAN table */}
|
||||
<Widget
|
||||
title="homelab/network"
|
||||
meta="8 isolated vlans · default deny inter-vlan"
|
||||
meta="8 network segments · default deny"
|
||||
as="section"
|
||||
>
|
||||
<div className="overflow-x-auto">
|
||||
@@ -163,7 +163,7 @@ export default function HomelabPage() {
|
||||
<thead>
|
||||
<tr className="border-b border-[var(--color-border)]">
|
||||
<th className="font-mono text-[var(--color-text-dim)] text-left py-qtr-lh pr-[3ch] uppercase">
|
||||
VLAN
|
||||
Segment
|
||||
</th>
|
||||
<th className="font-mono text-[var(--color-text-dim)] text-left py-qtr-lh pr-[3ch] uppercase">
|
||||
Name
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@ export default function RootLayout({
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<html lang="en" className="dark">
|
||||
<html lang="en" suppressHydrationWarning>
|
||||
<head>
|
||||
<ThemeScript />
|
||||
</head>
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
import type { Metadata } from "next";
|
||||
import Hero from "@/components/Hero";
|
||||
import Skills from "@/components/Skills";
|
||||
import Timeline from "@/components/Timeline";
|
||||
import ProjectCard from "@/components/ProjectCard";
|
||||
import Widget from "@/components/Widget";
|
||||
import { featuredProjects } from "@/data/projects";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Tyler Koenig",
|
||||
@@ -17,14 +13,6 @@ export default function Home() {
|
||||
<>
|
||||
<Hero />
|
||||
<Timeline />
|
||||
<Widget title="tyler/projects" badge={featuredProjects.length}>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-1ch">
|
||||
{featuredProjects.map((project) => (
|
||||
<ProjectCard key={project.slug} project={project} />
|
||||
))}
|
||||
</div>
|
||||
</Widget>
|
||||
<Skills />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
import type { Metadata } from "next";
|
||||
import Widget from "@/components/Widget";
|
||||
import ProjectCard from "@/components/ProjectCard";
|
||||
import { featuredProjects, archiveProjects } from "@/data/projects";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Projects | Tyler Koenig",
|
||||
description:
|
||||
"Featured projects and earlier work — homelab, open-pact, helm, and bootcamp/experiment archive.",
|
||||
};
|
||||
|
||||
export default function ProjectsPage() {
|
||||
return (
|
||||
<>
|
||||
<div className="mb-4lh">
|
||||
<p className="font-mono text-sm font-bold text-[var(--color-text)] mb-1lh">
|
||||
<span className="text-[var(--color-accent-green)] select-none mr-1ch" aria-hidden="true">❯</span>
|
||||
projects
|
||||
</p>
|
||||
<p className="font-mono text-sm text-[var(--color-text)] leading-relaxed max-w-xl opacity-80">
|
||||
Featured work first. Earlier experiments, browser extensions, and bootcamp projects below — kept for context.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<Widget title="projects/featured" badge={featuredProjects.length} as="section">
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-1ch">
|
||||
{featuredProjects.map((project) => (
|
||||
<ProjectCard key={project.slug} project={project} />
|
||||
))}
|
||||
</div>
|
||||
</Widget>
|
||||
|
||||
<Widget title="projects/archive" badge={archiveProjects.length} as="section">
|
||||
<div className="flex flex-col gap-px bg-[var(--color-border)]">
|
||||
{archiveProjects.map((project) => (
|
||||
<a
|
||||
key={project.slug}
|
||||
href={project.githubUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="bg-[var(--color-surface)] hover:bg-[var(--color-surface-raised)] flex items-start justify-between gap-2ch px-2ch py-1lh group"
|
||||
>
|
||||
<div className="flex flex-col gap-1ch flex-1 min-w-0">
|
||||
<div className="flex items-center gap-1ch">
|
||||
{project.year && (
|
||||
<span className="font-mono text-sm text-[var(--color-text-dim)] shrink-0">
|
||||
{project.year}
|
||||
</span>
|
||||
)}
|
||||
<span className="font-mono text-sm text-[var(--color-text)] group-hover:text-[var(--color-accent-green)] truncate">
|
||||
{project.title}
|
||||
</span>
|
||||
</div>
|
||||
<p className="font-mono text-sm text-[var(--color-text)] leading-relaxed opacity-75">
|
||||
{project.description}
|
||||
</p>
|
||||
<div className="flex flex-wrap gap-x-1ch gap-y-0.5">
|
||||
{project.tags.map((tag) => (
|
||||
<span key={tag} className="font-mono text-sm text-[var(--color-text-dim)]">
|
||||
{tag}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<span
|
||||
className="font-mono text-sm text-[var(--color-text-label)] group-hover:text-[var(--color-text)] shrink-0 mt-0.5"
|
||||
aria-hidden="true"
|
||||
>
|
||||
↗
|
||||
</span>
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</Widget>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -7,7 +7,7 @@ import { useTheme } from "@/context/ThemeContext";
|
||||
const links = [
|
||||
{ href: "/", label: "tyler" },
|
||||
{ href: "/homelab/", label: "homelab" },
|
||||
{ href: "/archive/", label: "archive" },
|
||||
{ href: "/projects/", label: "projects" },
|
||||
];
|
||||
|
||||
export default function Nav() {
|
||||
|
||||
+29
-29
@@ -17,22 +17,12 @@ export const projects: Project[] = [
|
||||
slug: "homelab",
|
||||
title: "homelab",
|
||||
description:
|
||||
"8-VLAN segmented network, Proxmox VMs/LXCs, SSO via Authentik, full monitoring stack (VictoriaMetrics + Grafana + Beszel + ntfy).",
|
||||
"7-VLAN segmented network, Wireguard VPN, Proxmox VMs/LXCs, SSO via Authentik, full monitoring stack (VictoriaMetrics + Grafana + Beszel + ntfy).",
|
||||
tags: ["Markdown", "Mermaid", "Proxmox", "Monitor", "Backup"],
|
||||
githubUrl: "https://gitea.lerkolabs.com/lerko/homelab",
|
||||
tier: "featured",
|
||||
year: 2026,
|
||||
},
|
||||
{
|
||||
slug: "open-pact",
|
||||
title: "open-pact",
|
||||
description:
|
||||
"Open protocol for AI agent identity, delegation, and portable memory. Ed25519 keypair identity, signed delegation warrants, portable signed memory facts. No central registry.",
|
||||
tags: ["TypeScript", "Ed25519", "DID", "npm", "CC0"],
|
||||
githubUrl: "https://github.com/lerko96/open-pact",
|
||||
tier: "featured",
|
||||
year: 2026,
|
||||
},
|
||||
{
|
||||
slug: "portfolio",
|
||||
title: "portfolio",
|
||||
@@ -43,6 +33,27 @@ export const projects: Project[] = [
|
||||
tier: "featured",
|
||||
year: 2021,
|
||||
},
|
||||
{
|
||||
slug: "nib",
|
||||
title: "nib",
|
||||
description:
|
||||
"Capture-first personal journal built with Go + React + SQLite. Currently developing in private when I have spare time.",
|
||||
tags: ["Go", "React", "SQLite", "Journal", "Stream-of-Thought"],
|
||||
githubUrl: "https://github.com/lerko96/nib",
|
||||
tier: "featured",
|
||||
year: 2026,
|
||||
},
|
||||
{
|
||||
slug: "open-pact",
|
||||
title: "open-pact",
|
||||
description:
|
||||
"Open protocol for AI agent identity, delegation, and portable memory. Ed25519 keypair identity, signed delegation warrants, portable signed memory facts. No central registry.",
|
||||
tags: ["TypeScript", "Ed25519", "DID", "npm", "CC0"],
|
||||
githubUrl: "https://github.com/lerko96/open-pact",
|
||||
tier: "featured",
|
||||
year: 2026,
|
||||
},
|
||||
// --- Archive ---
|
||||
{
|
||||
slug: "helm",
|
||||
title: "helm",
|
||||
@@ -50,20 +61,19 @@ export const projects: Project[] = [
|
||||
"Full-stack personal productivity dashboard. Go backend with chi router and SQLite, React + TypeScript frontend. Notes, todos, calendar (CalDAV), clipboard, bookmarks, memos. Self-hosted, single-user, daily use.",
|
||||
tags: ["Go", "React", "TypeScript", "SQLite", "CalDAV"],
|
||||
githubUrl: "https://github.com/lerko96/helm",
|
||||
tier: "featured",
|
||||
tier: "archive",
|
||||
year: 2026,
|
||||
},
|
||||
{
|
||||
slug: "claude-vault",
|
||||
title: "claude-vault",
|
||||
slug: "risk-ops",
|
||||
title: "risk-ops",
|
||||
description:
|
||||
"A scaffolding system for maintaining a living project knowledge base alongside a code repo, powered by Claude Code skills.",
|
||||
tags: ["Shell", "Developer-Tools", "Claude", "Knowledge-Management"],
|
||||
githubUrl: "https://github.com/lerko96/claude-vault",
|
||||
tier: "featured",
|
||||
"Browser-based strategy dashboard for Risk: Global Domination (SMG Studio). Open one HTML file — no install needed.",
|
||||
tags: ["HTML", "JavaScript"],
|
||||
githubUrl: "#",
|
||||
tier: "archive",
|
||||
year: 2026,
|
||||
},
|
||||
// --- Archive ---
|
||||
{
|
||||
slug: "golf-book-mobile",
|
||||
title: "golf-book-mobile",
|
||||
@@ -76,16 +86,6 @@ export const projects: Project[] = [
|
||||
statusBadge: "Pending App Store Approval",
|
||||
year: 2025,
|
||||
},
|
||||
{
|
||||
slug: "risk-ops",
|
||||
title: "risk-ops",
|
||||
description:
|
||||
"Browser-based strategy dashboard for Risk: Global Domination (SMG Studio). Open one HTML file — no install needed.",
|
||||
tags: ["HTML", "JavaScript"],
|
||||
githubUrl: "#",
|
||||
tier: "archive",
|
||||
year: 2026,
|
||||
},
|
||||
{
|
||||
slug: "twitter-thread-ext",
|
||||
title: "twitter-thread-ext",
|
||||
|
||||
+12
-4
@@ -47,12 +47,19 @@ export const timeline: TimelineEntry[] = [
|
||||
tags: ["go", "react", "typescript"],
|
||||
},
|
||||
{
|
||||
date: "2024-08",
|
||||
date: "2025",
|
||||
title: "Proxmox Backup Server",
|
||||
type: "homelab",
|
||||
description: "Deployed PBS on used desktop hardware for disaster recovery.",
|
||||
tags: ["backup", "recovery", "retention"],
|
||||
},
|
||||
{
|
||||
date: "2025",
|
||||
title: "Proxmox Cluster",
|
||||
type: "homelab",
|
||||
description:
|
||||
"Proxmox VMs/LXCs, SSO via Authentik, full monitoring stack (VictoriaMetrics + Grafana + Beszel + ntfy).",
|
||||
tags: ["proxmox", "networking", "monitoring", "sso"],
|
||||
"Proxmox installed on dedicated server and the fun begins. VMs/LXCs, SSO via Authentik, full monitoring stack (VictoriaMetrics + Grafana + Beszel + ntfy).",
|
||||
tags: ["proxmox", "containers", "VMs", "linux"],
|
||||
},
|
||||
{
|
||||
date: "2024-06",
|
||||
@@ -66,7 +73,8 @@ export const timeline: TimelineEntry[] = [
|
||||
date: "2024-03",
|
||||
title: "pfSense",
|
||||
type: "homelab",
|
||||
description: "Netgate pfSense n100 picked up on ebay.",
|
||||
description:
|
||||
"Netgate 1100 picked up on ebay to experience hands on networking configuration and troubleshooting.",
|
||||
tags: ["network", "firewall", "vlan", "dhcp"],
|
||||
},
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user