Initial Commit

This commit is contained in:
2026-02-22 09:44:19 +05:30
commit 8df4ef3b49
15 changed files with 4870 additions and 0 deletions

24
.gitignore vendored Normal file
View File

@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

3
README.md Normal file
View File

@@ -0,0 +1,3 @@
# Mi sitio personal
English or Spanish

23
eslint.config.js Normal file
View File

@@ -0,0 +1,23 @@
import js from '@eslint/js'
import globals from 'globals'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'
import tseslint from 'typescript-eslint'
import { defineConfig, globalIgnores } from 'eslint/config'
export default defineConfig([
globalIgnores(['dist']),
{
files: ['**/*.{ts,tsx}'],
extends: [
js.configs.recommended,
tseslint.configs.recommended,
reactHooks.configs.flat.recommended,
reactRefresh.configs.vite,
],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
},
},
])

13
index.html Normal file
View File

@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>portfolio</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>

3903
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

33
package.json Normal file
View File

@@ -0,0 +1,33 @@
{
"name": "portfolio",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
"lint": "eslint .",
"preview": "vite preview"
},
"dependencies": {
"lucide-react": "^0.575.0",
"react": "^19.2.0",
"react-dom": "^19.2.0"
},
"devDependencies": {
"@eslint/js": "^9.39.1",
"@tailwindcss/vite": "^4.2.0",
"@types/node": "^24.10.1",
"@types/react": "^19.2.7",
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^5.1.1",
"eslint": "^9.39.1",
"eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-refresh": "^0.4.24",
"globals": "^16.5.0",
"tailwindcss": "^4.2.0",
"typescript": "~5.9.3",
"typescript-eslint": "^8.48.0",
"vite": "^7.3.1"
}
}

6
src/App.css Normal file
View File

@@ -0,0 +1,6 @@
#root {
width: 100%;
margin: 0;
padding: 0;
text-align: left;
}

677
src/App.tsx Normal file
View File

@@ -0,0 +1,677 @@
import { useState, useEffect } from 'react';
import { Terminal, Github, Mail, Layout, Cpu, Network, ExternalLink, ChevronRight, Activity, User } from 'lucide-react';
import './App.css';
// --- THEME CONFIGURATION ---
const THEMES = [
{ name: 'Blueprint Cyan', value: '#38bdf8', id: 'cyan' },
{ name: 'Drafting Yellow', value: '#fde047', id: 'yellow' },
{ name: 'Red Ink', value: '#f87171', id: 'red' },
{ name: 'Chalk White', value: '#f8fafc', id: 'white' }
];
// --- MOCK DATA ---
const PROJECTS = [
{
id: 'kwin-tiling',
title: 'KWin Dynamic Tiling Script',
category: 'Window Management',
tech: ['JavaScript', 'KDE API', 'C++'],
desc: 'An i3-style tiling extension for KWin. Replaces standard floating windows with a deterministic BSP (Binary Space Partitioning) tree layout.',
icon: <Layout className="w-5 h-5" />
},
{
id: 'rp2040-firmware',
title: 'RP2040 Custom Keyboard & EMR',
category: 'Hardware / Firmware',
tech: ['C', 'Rust', 'PIO', 'I2C'],
desc: 'Custom firmware integrating a standard mechanical key matrix with an experimental EMR stylus digitizer for unified input processing.',
icon: <Cpu className="w-5 h-5" />
},
{
id: 'graphql-inspector',
title: 'GQL Traffic Interceptor',
category: 'Reverse Engineering',
tech: ['Go', 'eBPF', 'React'],
desc: 'A local network utility utilizing eBPF to silently inspect, decode, and visualize GraphQL traffic between local containers.',
icon: <Network className="w-5 h-5" />
}
];
const LAB_NOTES = [
{ date: '2026-02-14', title: 'Reverse Engineering TrueNAS Middleware API' },
{ date: '2026-01-28', title: 'Why I stopped using Nginx and wrote my own router' },
{ date: '2025-12-10', title: 'Injecting dynamic themes via CSS variable mutations' }
];
export default function App() {
const [activeTheme, setActiveTheme] = useState(THEMES[0]);
const [mousePos, setMousePos] = useState({ x: 0, y: 0 });
// Update CSS variables for dynamic theming
useEffect(() => {
document.documentElement.style.setProperty('--color-accent', activeTheme.value);
// Calculate a semi-transparent version for backgrounds/borders
document.documentElement.style.setProperty('--color-accent-muted', `${activeTheme.value}33`);
}, [activeTheme]);
// Subtle interactive background effect
useEffect(() => {
const handleMouseMove = (e: MouseEvent) => {
setMousePos({ x: e.clientX, y: e.clientY });
};
window.addEventListener('mousemove', handleMouseMove);
return () => window.removeEventListener('mousemove', handleMouseMove);
}, []);
return (
<div className="min-h-screen bg-[var(--bg-base)] text-[var(--text-main)] font-mono selection:bg-[var(--color-accent)] selection:text-[var(--bg-base)] relative overflow-x-hidden">
{/* Global CSS for the dynamic theme and indie style */}
<style>{`
@import url('https://fonts.googleapis.com/css2?family=Architects+Daughter&family=Space+Mono:ital,wght@0,400;0,700;1,400&display=swap');
:root {
/* =========================================
🎨 CENTRAL COLOR PALETTE
Modify these variables to change the theme!
========================================= */
/* Backgrounds */
--bg-base: #111113; /* Main page background */
--bg-surface: rgba(24, 24, 27, 0.4); /* Cards and secondary backgrounds */
--bg-panel: rgba(9, 9, 11, 0.85); /* Terminal panes and rigid components */
/* Typography */
--text-main: #f4f4f5; /* Headings and primary text */
--text-muted: #a1a1aa; /* Paragraphs and secondary text */
--text-dim: #52525b; /* Annotations and subtle details */
/* Structure */
--border-main: #27272a; /* Dashed lines and pane borders */
--grid-pattern: rgba(255, 255, 255, 0.08); /* Background dot grid color */
/* Status Indicators */
--status-success: #34d399; /* Emerald color for 'Online' status */
/* Easter Egg (Cat) */
--cat-body: #111113; /* Main body color (usually matches bg-base) */
--cat-stroke: #60a5fa; /* Blue stroke outline */
--cat-nose: #f472b6; /* Pink nose */
--cat-eyes: #ffffff; /* White eyes */
--heart-color: #f472b6; /* Heart animation color */
}
.font-sketch { font-family: 'Architects Daughter', cursive; }
.font-mono { font-family: 'Space Mono', monospace; }
.accent-text { color: var(--color-accent); transition: color 0.3s ease; }
.accent-bg { background-color: var(--color-accent); transition: background-color 0.3s ease; }
/* Indie / Hand-drawn borders */
.sketch-border {
border: 2px solid var(--color-accent-muted);
border-radius: 255px 15px 225px 15px/15px 225px 15px 255px;
transition: all 0.3s ease;
}
.sketch-border:hover {
border-color: var(--color-accent);
border-radius: 15px 225px 15px 255px/255px 15px 225px 15px;
}
.sketch-border-subtle {
border: 1px solid var(--border-main);
border-radius: 2px 255px 3px 25px / 255px 5px 225px 3px;
transition: all 0.3s ease;
}
.sketch-border-subtle:hover {
border-color: var(--color-accent);
border-radius: 255px 5px 225px 3px / 2px 255px 3px 25px;
}
.dot-grid {
background-image: radial-gradient(var(--grid-pattern) 1px, transparent 1px);
background-size: 24px 24px;
}
`}</style>
{/* SVG Noise Filter for rough paper texture */}
<svg className="hidden">
<filter id="noiseFilter">
<feTurbulence type="fractalNoise" baseFrequency="0.7" numOctaves="3" stitchTiles="stitch" />
</filter>
</svg>
<div className="fixed inset-0 z-0 pointer-events-none opacity-[0.04]" style={{ filter: 'url(#noiseFilter)' }} />
{/* Blueprint Dot Grid Background */}
<div className="fixed inset-0 dot-grid z-0 pointer-events-none opacity-50" />
{/* Subtle mouse glow */}
<div
className="fixed inset-0 z-0 pointer-events-none opacity-20 transition-opacity duration-300"
style={{
background: `radial-gradient(400px circle at ${mousePos.x}px ${mousePos.y}px, var(--color-accent-muted), transparent 40%)`
}}
/>
{/* Hidden Cat Easter Egg */}
<CatEasterEgg />
<div className="relative z-10 max-w-5xl mx-auto px-6 py-12 flex flex-col gap-16">
{/* Navigation / Command Bar */}
<nav className="flex flex-col sm:flex-row justify-between items-start sm:items-center border-b-2 border-dashed border-[var(--border-main)] pb-4 gap-4">
<div className="flex items-center gap-2 text-sm text-[var(--text-muted)] relative group">
<Terminal className="w-5 h-5 accent-text transform -rotate-12 group-hover:rotate-12 transition-transform" />
<span className="font-sketch text-2xl text-[var(--text-main)] tracking-wide">aditya_gupta <span className="text-[var(--text-dim)]">//</span> homelab</span>
{/* Hand-drawn annotation */}
<div className="absolute -top-6 -right-16 font-sketch text-sm accent-text rotate-12 flex items-center gap-1 opacity-80">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="transform -scale-x-100 rotate-45"><path d="M5 12h14" /><path d="m12 5 7 7-7 7" /></svg>
<span>root</span>
</div>
</div>
<div className="flex items-center gap-6">
<div className="flex items-center gap-2">
<span className="font-sketch text-[var(--text-dim)] text-sm rotate-[-2deg]">theme_picker:</span>
<div className="flex gap-2">
{THEMES.map(theme => (
<button
key={theme.id}
onClick={() => setActiveTheme(theme)}
title={`Switch to ${theme.name}`}
className={`w-5 h-5 transition-all duration-200 sketch-border-subtle ${activeTheme.id === theme.id ? 'scale-125 shadow-[0_0_10px_var(--color-accent-muted)]' : 'hover:scale-110'}`}
style={{ backgroundColor: theme.value, borderColor: activeTheme.id === theme.id ? theme.value : 'transparent' }}
aria-label={`Switch theme to ${theme.name}`}
/>
))}
</div>
</div>
<div className="h-6 w-px border-l-2 border-dashed border-[var(--border-main)]" />
<a href="#" className="font-sketch text-lg hover:text-[var(--text-main)] transition-colors flex items-center gap-2 group"><Github className="w-4 h-4 group-hover:-translate-y-1 transition-transform" /> github</a>
<a href="#" className="font-sketch text-lg hover:text-[var(--text-main)] transition-colors flex items-center gap-2 group"><Mail className="w-4 h-4 group-hover:-translate-y-1 transition-transform" /> contact</a>
</div>
</nav>
{/* Hero Section */}
<header className="flex flex-col md:flex-row gap-12 items-center justify-between relative">
<div className="space-y-6 max-w-2xl">
<div className="inline-flex items-center gap-2 px-4 py-1.5 sketch-border bg-[var(--bg-surface)] text-xs mb-4 transform -rotate-1">
<Activity className="w-3 h-3 accent-text animate-pulse" />
<span className="font-sketch text-sm">STATUS: <span className="text-[var(--text-main)]">ONLINE</span></span>
</div>
<h1 className="text-4xl sm:text-5xl font-bold tracking-tight text-[var(--text-main)] leading-tight">
I engineer <span className="accent-text relative inline-block">
infrastructure
<svg className="absolute w-full h-3 -bottom-1 left-0 accent-text" viewBox="0 0 100 20" preserveAspectRatio="none" fill="none" stroke="currentColor" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round"><path d="M2,15 Q50,-5 98,15" /></svg>
</span>,<br /> reverse-engineer black boxes,<br /> and build tools.
</h1>
<p className="text-lg text-[var(--text-muted)] leading-relaxed max-w-xl font-mono text-sm">
Based in New Delhi. Exploring homelab orchestration, low-level input devices, and dynamic theming engines. This is my public scratchpad and systems notebook.
</p>
</div>
{/* Image Placeholder */}
<div className="relative w-48 h-56 sm:w-64 sm:h-72 shrink-0 sketch-border bg-[var(--bg-panel)] transform rotate-[3deg] hover:rotate-0 transition-transform duration-500 group flex flex-col items-center justify-center mt-8 md:mt-0">
{/* Faux tape in corners */}
<div className="absolute -top-3 -right-4 w-12 h-6 bg-[var(--color-accent-muted)] transform rotate-45 opacity-60 backdrop-blur-sm sketch-border-subtle border-none" />
<div className="absolute -bottom-3 -left-4 w-12 h-6 bg-[var(--color-accent-muted)] transform rotate-45 opacity-60 backdrop-blur-sm sketch-border-subtle border-none" />
<div className="absolute inset-2 border-2 border-dashed border-[var(--border-main)] flex flex-col items-center justify-center text-[var(--text-dim)] group-hover:text-[var(--color-accent)] transition-colors duration-300 group-hover:bg-[var(--color-accent-muted)]/5">
<User className="w-12 h-12 mb-3 opacity-50" />
<span className="font-sketch text-sm">avatar.jpg</span>
<span className="font-mono text-[10px] opacity-40 mt-1">[1024x1024]</span>
</div>
{/* Hand-drawn annotation */}
<div className="absolute -bottom-8 -left-12 font-sketch text-xs accent-text -rotate-[15deg] hidden md:flex items-center gap-2 opacity-80">
<span className="whitespace-nowrap">the operator</span>
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="transform -scale-y-100 rotate-45"><path d="M5 12h14" /><path d="m12 5 7 7-7 7" /></svg>
</div>
</div>
</header>
{/* System Overview Dashboard (Replaced Interactive Tiling Demo) */}
<section className="space-y-4 relative mt-12 md:mt-0">
<div className="absolute -left-12 top-10 font-sketch accent-text -rotate-90 hidden lg:block tracking-widest text-sm opacity-60">
[ SYSTEM_SPECS ]
</div>
<div className="flex items-center justify-between border-b-2 border-dashed border-[var(--border-main)] pb-2">
<h2 className="font-sketch text-2xl text-[var(--text-main)] flex items-center gap-2">
<Cpu className="w-5 h-5 accent-text" /> system_overview.sh
</h2>
<span className="font-sketch text-[var(--text-dim)] text-sm animate-pulse">~ click panes to drill down ~</span>
</div>
<InteractiveSystemOverview />
</section>
<div className="grid grid-cols-1 md:grid-cols-3 gap-12 mt-8">
{/* Featured Projects */}
<section className="md:col-span-2 space-y-6">
<h2 className="font-sketch text-2xl text-[var(--text-main)] border-b-2 border-dashed border-[var(--border-main)] pb-2 flex items-center gap-2">
<Layout className="w-5 h-5 accent-text" /> architecture_studies/
</h2>
<div className="flex flex-col gap-6">
{PROJECTS.map((project, idx) => (
<article
key={project.id}
className={`group relative p-6 bg-[var(--bg-surface)] sketch-border-subtle cursor-pointer overflow-hidden ${idx % 2 === 0 ? 'transform rotate-[0.5deg]' : 'transform -rotate-[0.5deg]'}`}
>
<div className="absolute top-0 left-0 w-full h-1 bg-[var(--border-main)] group-hover:bg-[var(--color-accent)] transition-colors duration-300 opacity-50" />
<div className="flex justify-between items-start mb-4">
<div className="flex items-center gap-4">
<div className="p-3 sketch-border-subtle bg-[var(--bg-panel)] text-[var(--text-muted)] group-hover:text-[var(--color-accent)] group-hover:scale-110 transition-all transform -rotate-3">
{project.icon}
</div>
<div>
<h3 className="text-[var(--text-main)] font-bold text-lg flex items-center gap-2 font-mono">
{project.title}
<ExternalLink className="w-4 h-4 opacity-0 group-hover:opacity-100 transition-opacity accent-text" />
</h3>
<span className="font-sketch text-sm text-[var(--text-muted)] accent-text">{project.category}</span>
</div>
</div>
</div>
<p className="text-sm text-[var(--text-muted)] mb-5 leading-relaxed font-mono">
{project.desc}
</p>
<div className="flex flex-wrap gap-2">
{project.tech.map(t => (
<span key={t} className="text-xs px-2 py-1 bg-[var(--bg-panel)] sketch-border-subtle text-[var(--text-muted)] font-mono">
{t}
</span>
))}
</div>
</article>
))}
</div>
</section>
{/* Lab Notes */}
<section className="space-y-6">
<h2 className="font-sketch text-2xl text-[var(--text-main)] border-b-2 border-dashed border-[var(--border-main)] pb-2 flex items-center gap-2">
<ChevronRight className="w-5 h-5 accent-text" /> lab_notes.txt
</h2>
<div className="flex flex-col gap-4 border-l-2 border-dashed border-[var(--border-main)] ml-2 relative">
{/* Hand drawn arrow pointing to notes */}
<div className="absolute -left-10 top-0 font-sketch text-xs accent-text -rotate-12 hidden sm:block">
recent
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="ml-2 transform rotate-90"><path d="M5 12h14" /><path d="m12 5 7 7-7 7" /></svg>
</div>
{LAB_NOTES.map((note, i) => (
<div key={i} className="relative pl-6 py-2 group cursor-pointer">
{/* Rough Timeline dot */}
<div className="absolute left-[-9px] top-[14px] w-[16px] h-[16px] flex items-center justify-center bg-[var(--bg-base)] group-hover:scale-125 transition-transform">
<span className="text-[var(--text-dim)] group-hover:text-[var(--color-accent)] font-sketch text-lg leading-none transform -rotate-12">x</span>
</div>
<div className="font-mono text-[10px] text-[var(--text-dim)] mb-1">{note.date}</div>
<h3 className="text-sm font-mono text-[var(--text-muted)] group-hover:text-[var(--text-main)] transition-colors leading-relaxed">
{note.title}
</h3>
</div>
))}
</div>
<div className="mt-10 p-5 sketch-border bg-[var(--color-accent-muted)]/5 transform rotate-1">
<div className="flex gap-3 items-start">
<Terminal className="w-5 h-5 accent-text mt-1 flex-shrink-0" />
<p className="text-[var(--text-muted)] text-sm font-mono leading-relaxed">
<span className="font-sketch text-lg accent-text block mb-1">Observation:</span>
All systems operating nominally. Proceeding with hardware teardown sequence 0x4B.
</p>
</div>
</div>
</section>
</div>
{/* Footer */}
<footer className="border-t-2 border-dashed border-[var(--border-main)] pt-8 pb-12 flex flex-col sm:flex-row justify-between items-center gap-4 text-xs font-mono text-[var(--text-dim)]">
<div className="flex items-center gap-2">
<span className="font-sketch text-lg transform -rotate-6 block">Aditya Gupta</span>
<span>// 2026 // NEW_DELHI</span>
</div>
<div className="flex gap-4 sketch-border-subtle px-4 py-2 bg-[var(--bg-surface)]">
<span>UPTIME: 99.9%</span>
<span className="text-[var(--text-dim)]">|</span>
<span className="accent-text">NOMINAL</span>
</div>
</footer>
</div>
</div>
);
}
// --- INTERACTIVE SYSTEM OVERVIEW COMPONENT ---
// Progressively reveals more specific content as panes are "split"
const CONTENT_MODULES = [
{
id: 'bio',
title: '/home/aditya/bio.txt',
tty: 'tty1',
content: (
<div className="font-mono text-sm sm:text-base text-[var(--text-muted)] leading-relaxed flex-1 space-y-4">
<div>
<span className="accent-text font-sketch text-xl mr-2">{'>'}</span>
<span className="text-[var(--text-main)]">whoami</span>
<p className="mt-2 ml-4 border-l-2 border-[var(--border-main)] pl-4 text-[var(--text-main)]">
Aditya Gupta. 20. Based in New Delhi. <br />
I don't just write code; I build and dismantle systems.
</p>
</div>
<div>
<span className="accent-text font-sketch text-xl mr-2">{'>'}</span>
<span className="text-[var(--text-main)]">cat mindset.md</span>
<div className="mt-2 ml-4 space-y-2 border-l-2 border-[var(--border-main)] pl-4">
<p>Treat the environment as a continuous experiment.</p>
<p className="text-xs text-[var(--text-dim)] mt-2">Split this pane to view technical stack.</p>
</div>
</div>
</div>
)
},
{
id: 'skills',
title: 'ls /usr/bin/skills',
tty: 'tty2',
content: (
<div className="font-mono text-xs text-[var(--text-main)] flex-1 content-start">
<div className="flex flex-wrap gap-2 mt-1">
{['Rust', 'Go', 'C/C++', 'eBPF', 'Kubernetes', 'TrueNAS', 'Linux/KWin', 'React'].map((skill) => (
<span key={skill} className="px-2 py-1 bg-[var(--bg-surface)] border border-[var(--border-main)] rounded-sm hover:border-[var(--color-accent)] hover:text-[var(--color-accent)] transition-colors cursor-default">
{skill}
</span>
))}
</div>
<p className="mt-4 text-[10px] text-[var(--text-dim)] animate-pulse border-t border-dashed border-[var(--border-main)] pt-2">Split for infrastructure status...</p>
</div>
)
},
{
id: 'homelab',
title: 'kubectl get nodes',
tty: 'tty3',
content: (
<div className="font-mono text-[10px] sm:text-xs text-[var(--text-muted)] flex-1 overflow-x-auto">
<table className="w-full text-left border-collapse">
<thead>
<tr className="text-[var(--text-dim)] border-b border-[var(--border-main)]">
<th className="pb-1 font-normal">NAME</th>
<th className="pb-1 font-normal">STATUS</th>
<th className="pb-1 font-normal">ROLES</th>
</tr>
</thead>
<tbody className="leading-relaxed">
<tr className="group/row hover:bg-[var(--bg-surface)]">
<td className="pt-1 text-[var(--text-main)]">pve-core</td>
<td className="pt-1"><span className="text-[var(--status-success)]">Ready</span></td>
<td className="pt-1 accent-text">control-plane</td>
</tr>
<tr className="group/row hover:bg-[var(--bg-surface)]">
<td>nas-store</td>
<td><span className="text-[var(--status-success)]">Ready</span></td>
<td>truenas-iscsi</td>
</tr>
<tr className="group/row hover:bg-[var(--bg-surface)]">
<td>edge-rout</td>
<td><span className="text-[var(--status-success)]">Ready</span></td>
<td>ingress-bpf</td>
</tr>
</tbody>
</table>
<div className="mt-2 flex items-center justify-between">
<div className="flex items-center gap-2">
<span className="w-2 h-2 rounded-full bg-[var(--status-success)]"></span>
<span className="font-sketch text-xs text-[var(--text-dim)]">Healthy</span>
</div>
</div>
</div>
)
},
{
id: 'hardware',
title: 'lshw -short | grep input',
tty: 'tty4',
content: (
<div className="font-mono text-xs text-[var(--text-muted)] flex-1">
<span className="accent-text font-sketch text-lg mr-2">{'>'}</span>
<span className="text-[var(--text-main)]">Hardware focus</span>
<ul className="mt-2 ml-2 border-l-2 border-[var(--border-main)] pl-3 space-y-1.5">
<li>MCU: RP2040 (C/Rust)</li>
<li>Matrix: Ortho 40%</li>
<li>Sensor: EMR Stylus</li>
<li>Rate: 1000Hz</li>
</ul>
</div>
)
},
{
id: 'focus',
title: 'tail -f /var/log/focus',
tty: 'tty5',
content: (
<div className="font-mono text-xs text-[var(--text-muted)] flex-1">
<div className="text-[var(--text-dim)] border-b border-dashed border-[var(--border-main)] pb-1 mb-2">Streaming...</div>
<p className="mb-1"><span className="accent-text">[INFO]</span> Analyzing GQL via eBPF</p>
<p className="mb-1"><span className="accent-text">[INFO]</span> Wiring custom router</p>
<p><span className="text-[var(--text-dim)]">[WAIT]</span> PCB transit delays</p>
</div>
)
}
];
function InteractiveSystemOverview() {
const [activeNodes, setActiveNodes] = useState([CONTENT_MODULES[0]]);
const [nextModuleIndex, setNextModuleIndex] = useState(1);
const handleSplit = (clickedIndex: number) => {
if (nextModuleIndex >= CONTENT_MODULES.length) return; // Max panes reached
setActiveNodes(prev => {
const newNodes = [...prev];
newNodes.splice(clickedIndex + 1, 0, CONTENT_MODULES[nextModuleIndex]);
return newNodes;
});
setNextModuleIndex(prev => prev + 1);
};
const getGridContainerClass = () => {
// We use a 12-column grid to allow highly precise asymmetrical splits
return "grid grid-cols-1 sm:grid-cols-12 gap-3 transition-all duration-500 ease-in-out p-2 auto-rows-[minmax(180px,auto)]";
};
const getNodeClass = (length: number, index: number) => {
// 1 Pane: Full width, taller
if (length === 1) return "sm:col-span-12 sm:row-span-2 min-h-[300px]";
// 2 Panes: 50/50 split
if (length === 2) return "sm:col-span-6 sm:row-span-2 min-h-[300px]";
// 3 Panes: 1 large left (spans 2 rows), 2 stacked on the right
if (length === 3) {
if (index === 0) return "sm:col-span-8 sm:row-span-2";
if (index === 1) return "sm:col-span-4 sm:row-span-1";
if (index === 2) return "sm:col-span-4 sm:row-span-1";
}
// 4 Panes: 2x2 grid
if (length === 4) return "sm:col-span-6 sm:row-span-1 min-h-[220px]";
// 5 Panes: Advanced Bento/Tiling layout (expands downward)
if (length === 5) {
if (index === 0) return "sm:col-span-7 sm:row-span-2"; // Bio: Top left, large
if (index === 1) return "sm:col-span-5 sm:row-span-1"; // Skills: Top right
if (index === 2) return "sm:col-span-5 sm:row-span-1"; // Homelab: Middle right
if (index === 3) return "sm:col-span-6 sm:row-span-1 min-h-[200px]"; // Hardware: Bottom left
if (index === 4) return "sm:col-span-6 sm:row-span-1 min-h-[200px]"; // Focus: Bottom right
}
return "";
};
return (
<div className={getGridContainerClass()}>
{activeNodes.map((node, i) => {
const rotation = i % 2 === 0 ? 'rotate-[0.5deg]' : '-rotate-[0.5deg]';
return (
<div
key={node.id}
onClick={() => handleSplit(i)}
className={`
sketch-border bg-[var(--bg-panel)] p-4 relative group overflow-hidden flex flex-col
transform ${rotation} transition-all duration-500 hover:scale-[1.01] hover:z-10
${nextModuleIndex < CONTENT_MODULES.length ? 'cursor-crosshair' : 'cursor-default'}
${getNodeClass(activeNodes.length, i)}
`}
>
{/* Top Bar */}
<div className="flex justify-between items-center border-b-2 border-dashed border-[var(--border-main)] pb-2 mb-3 shrink-0">
<span className="font-sketch text-xs sm:text-sm text-[var(--text-muted)] lowercase flex items-center gap-1.5 whitespace-nowrap overflow-hidden text-ellipsis">
<span className="text-base accent-text leading-none">*</span>
{node.title}
</span>
<span className="font-sketch text-[10px] sm:text-xs text-[var(--text-dim)] shrink-0 ml-2">[{node.tty}]</span>
</div>
{/* Dynamic Content */}
<div className="flex-1 overflow-y-auto custom-scrollbar pr-1">
{node.content}
</div>
{/* Hover Overlay indicating split action (only if more modules exist) */}
{nextModuleIndex < CONTENT_MODULES.length && (
<div className="absolute inset-0 bg-[var(--color-accent-muted)]/5 opacity-0 group-hover:opacity-100 transition-opacity flex items-center justify-center backdrop-blur-[1px] pointer-events-none">
<span className="font-sketch text-lg sm:text-xl font-bold text-[var(--text-main)] bg-[var(--bg-panel)] px-3 py-1.5 sketch-border transform rotate-[-2deg] shadow-lg">
SPLIT_VIEW
</span>
</div>
)}
</div>
);
})}
</div>
);
}
// --- EASTER EGG COMPONENT ---
function CatEasterEgg() {
const [reveal, setReveal] = useState(0); // 0 (hidden) to 100 (fully visible)
const [posX, setPosX] = useState(80); // percentage from left
const [showHeart, setShowHeart] = useState(false);
useEffect(() => {
let currentReveal = 0;
const handleScroll = () => {
if (showHeart) return; // Prevent scroll from overriding the heart animation
const currentScrollY = window.scrollY;
// Define the specific "sliver" of scroll where the cat lives
const period = 1200; // The pattern repeats every 1200px of scrolling
const peakOffset = 700; // The exact scroll Y position where the cat is 100% visible
const peekWidth = 300; // How many pixels of scrolling it takes to fully reveal/hide
// Calculate how far the current scroll is from the peak visibility point
const distanceToPeak = Math.abs((currentScrollY % period) - peakOffset);
let newReveal = 0;
if (distanceToPeak < peekWidth) {
// Linearly map the distance to a 0-100 percentage
newReveal = 100 - (distanceToPeak / peekWidth) * 100;
}
// If it just fully hid, randomize its next horizontal position
if (newReveal === 0 && currentReveal > 0) {
setPosX(Math.floor(Math.random() * 80) + 10); // Random position between 10% and 90%
}
currentReveal = newReveal;
setReveal(newReveal);
};
window.addEventListener('scroll', handleScroll, { passive: true });
handleScroll(); // Check initial position in case the user reloads halfway down
return () => {
window.removeEventListener('scroll', handleScroll);
};
}, [showHeart]); // Depend on showHeart
// Handle click: show heart, wait, then hide
const handleCatClick = () => {
if (showHeart || reveal === 0) return;
setShowHeart(true);
// Temporarily force it fully visible for the animation
setReveal(100);
// Wait 1.2s to show the heart, then hide the cat
setTimeout(() => {
setShowHeart(false);
setReveal(0);
// Randomize position after hiding
setTimeout(() => setPosX(Math.floor(Math.random() * 80) + 10), 500);
}, 1200);
};
return (
<div
className="fixed bottom-[-2px] z-50 transition-transform duration-100 ease-out"
style={{
left: `${posX}%`,
transform: `translateY(${150 - (reveal * 1.5)}%)` // 150% is fully hidden, 0% is fully visible
}}
>
<div className="relative group cursor-pointer" onClick={handleCatClick}>
{/* Floating Heart Animation */}
<div className={`absolute left-1/2 transform -translate-x-1/2 text-[var(--heart-color)] text-4xl z-50 transition-all duration-700 ease-out pointer-events-none ${showHeart ? '-top-16 opacity-100 scale-110' : 'top-2 opacity-0 scale-50'}`}>
</div>
{/* Hand-drawn annotation on hover */}
<div className="absolute -top-10 -left-20 font-sketch text-xs accent-text -rotate-12 opacity-0 group-hover:opacity-100 transition-opacity whitespace-nowrap bg-[var(--bg-panel)] px-3 py-1 sketch-border-subtle backdrop-blur-sm shadow-xl">
./purr.sh
</div>
{/* Paws */}
<div className="absolute -top-2 left-2 w-4 h-5 border-2 border-[var(--cat-stroke)] rounded-t-[12px] bg-[var(--cat-body)] z-10"></div>
<div className="absolute -top-2 right-2 w-4 h-5 border-2 border-[var(--cat-stroke)] rounded-t-[12px] bg-[var(--cat-body)] z-10"></div>
{/* Head */}
<div className="w-16 h-14 border-2 border-[var(--cat-stroke)] border-b-0 rounded-t-[24px] bg-[var(--cat-body)] flex justify-center pt-5 gap-3 relative z-0">
{/* Ears */}
<div className="absolute -top-3 left-0 w-6 h-7 border-2 border-[var(--cat-stroke)] border-b-0 border-r-0 rounded-tl-full transform rotate-[20deg] bg-[var(--cat-body)] -z-10"></div>
<div className="absolute -top-3 right-0 w-6 h-7 border-2 border-[var(--cat-stroke)] border-b-0 border-l-0 rounded-tr-full transform -rotate-[20deg] bg-[var(--cat-body)] -z-10"></div>
{/* Whiskers */}
<div className="absolute top-7 -left-3 w-4 h-[2px] bg-[var(--cat-stroke)] rounded-full transform rotate-[15deg] opacity-60"></div>
<div className="absolute top-9 -left-3 w-4 h-[2px] bg-[var(--cat-stroke)] rounded-full transform -rotate-[5deg] opacity-60"></div>
<div className="absolute top-7 -right-3 w-4 h-[2px] bg-[var(--cat-stroke)] rounded-full transform -rotate-[15deg] opacity-60"></div>
<div className="absolute top-9 -right-3 w-4 h-[2px] bg-[var(--cat-stroke)] rounded-full transform rotate-[5deg] opacity-60"></div>
{/* Eyes */}
<div className={`w-3 h-3 rounded-full bg-[var(--cat-eyes)] relative transition-all duration-300 ${showHeart ? 'scale-y-[0.2]' : ''}`}>
</div>
<div className={`w-3 h-3 rounded-full bg-[var(--cat-eyes)] relative transition-all duration-300 ${showHeart ? 'scale-y-[0.2]' : ''}`}>
</div>
{/* Nose/Mouth */}
<div className="absolute top-8 left-1/2 transform -translate-x-1/2 w-1.5 h-1 bg-[var(--cat-nose)] rounded-full"></div>
<div className="absolute top-[34px] left-1/2 transform -translate-x-1/2 text-[var(--cat-stroke)] text-[10px] font-bold tracking-tighter">w</div>
</div>
</div>
</div>
);
}

13
src/index.css Normal file
View File

@@ -0,0 +1,13 @@
@import "tailwindcss";
/* Keep this file minimal: Tailwind provides base styling.
App/theme styles live in `src/App.css` and `src/theme.css`. */
html,
body {
height: 100%;
}
body {
margin: 0;
}

11
src/main.tsx Normal file
View File

@@ -0,0 +1,11 @@
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import './theme.css'
import App from './App.tsx'
createRoot(document.getElementById('root')!).render(
<StrictMode>
<App />
</StrictMode>,
)

92
src/theme.css Normal file
View File

@@ -0,0 +1,92 @@
@import url('https://fonts.googleapis.com/css2?family=Architects+Daughter&family=Space+Mono:ital,wght@0,400;0,700;1,400&display=swap');
:root {
/* =========================================
🎨 CENTRAL COLOR PALETTE
Modify these variables to change the theme!
========================================= */
/* Backgrounds */
--bg-base: #111113;
/* Main page background */
--bg-surface: rgba(24, 24, 27, 0.4);
/* Cards and secondary backgrounds */
--bg-panel: rgba(9, 9, 11, 0.85);
/* Terminal panes and rigid components */
/* Typography */
--text-main: #f4f4f5;
/* Headings and primary text */
--text-muted: #a1a1aa;
/* Paragraphs and secondary text */
--text-dim: #52525b;
/* Annotations and subtle details */
/* Structure */
--border-main: #27272a;
/* Dashed lines and pane borders */
--grid-pattern: rgba(255, 255, 255, 0.08);
/* Background dot grid color */
/* Status Indicators */
--status-success: #34d399;
/* Emerald color for 'Online' status */
/* Easter Egg (Cat) */
--cat-body: #111113;
/* Main body color (usually matches bg-base) */
--cat-stroke: #60a5fa;
/* Blue stroke outline */
--cat-nose: #f472b6;
/* Pink nose */
--cat-eyes: #ffffff;
/* White eyes */
--heart-color: #f472b6;
/* Heart animation color */
}
.font-sketch {
font-family: 'Architects Daughter', cursive;
}
.font-mono {
font-family: 'Space Mono', monospace;
}
.accent-text {
color: var(--color-accent);
transition: color 0.3s ease;
}
.accent-bg {
background-color: var(--color-accent);
transition: background-color 0.3s ease;
}
/* Indie / Hand-drawn borders */
.sketch-border {
border: 2px solid var(--color-accent-muted);
border-radius: 255px 15px 225px 15px/15px 225px 15px 255px;
transition: all 0.3s ease;
}
.sketch-border:hover {
border-color: var(--color-accent);
border-radius: 15px 225px 15px 255px/255px 15px 225px 15px;
}
.sketch-border-subtle {
border: 1px solid var(--border-main);
border-radius: 2px 255px 3px 25px / 255px 5px 225px 3px;
transition: all 0.3s ease;
}
.sketch-border-subtle:hover {
border-color: var(--color-accent);
border-radius: 255px 5px 225px 3px / 2px 255px 3px 25px;
}
.dot-grid {
background-image: radial-gradient(var(--grid-pattern) 1px, transparent 1px);
background-size: 24px 24px;
}

28
tsconfig.app.json Normal file
View File

@@ -0,0 +1,28 @@
{
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
"target": "ES2022",
"useDefineForClassFields": true,
"lib": ["ES2022", "DOM", "DOM.Iterable"],
"module": "ESNext",
"types": ["vite/client"],
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"moduleDetection": "force",
"noEmit": true,
"jsx": "react-jsx",
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"erasableSyntaxOnly": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true
},
"include": ["src"]
}

7
tsconfig.json Normal file
View File

@@ -0,0 +1,7 @@
{
"files": [],
"references": [
{ "path": "./tsconfig.app.json" },
{ "path": "./tsconfig.node.json" }
]
}

26
tsconfig.node.json Normal file
View File

@@ -0,0 +1,26 @@
{
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
"target": "ES2023",
"lib": ["ES2023"],
"module": "ESNext",
"types": ["node"],
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"moduleDetection": "force",
"noEmit": true,
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"erasableSyntaxOnly": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true
},
"include": ["vite.config.ts"]
}

11
vite.config.ts Normal file
View File

@@ -0,0 +1,11 @@
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import tailwindcss from '@tailwindcss/vite'
// https://vite.dev/config/
export default defineConfig({
plugins: [
react(),
tailwindcss(),
],
})