Files
portfolio/app/components/KernelPanic404.tsx
denshooter 947f72ecca
Some checks failed
Production Deployment (Zero Downtime) / deploy-production (push) Has been cancelled
feat: Add interactive kernel panic 404 page
- Terminal-style 404 page with boot sequence
- Interactive command line with file system
- Easter eggs: hawkins/011, fsociety, 42, rm -rf /
- CRT monitor effects and visual glitches
- Audio synthesis for key presses and effects
- Full terminal emulator with commands: ls, cd, cat, grep, etc.
2026-01-09 19:26:08 +01:00

704 lines
24 KiB
TypeScript

"use client";
import { useEffect, useRef, useState } from "react";
interface FileSystemNode {
type: 'file' | 'dir' | 'exe';
content?: string;
children?: Record<string, FileSystemNode>;
}
export default function KernelPanic404() {
const outputRef = useRef<HTMLDivElement>(null);
const inputRef = useRef<HTMLInputElement>(null);
const inputContainerRef = useRef<HTMLDivElement>(null);
const [systemFrozen, setSystemFrozen] = useState(false);
const [currentMusic, setCurrentMusic] = useState<{ stop?: () => void } | null>(null);
const [hawkinsActive, setHawkinsActive] = useState(false);
const [fsocietyActive, setFsocietyActive] = useState(false);
const [commandHistory, setCommandHistory] = useState<string[]>([]);
const [historyIndex, setHistoryIndex] = useState(-1);
const [currentPath, setCurrentPath] = useState<FileSystemNode | null>(null);
const [pathStr, setPathStr] = useState("~");
const audioCtxRef = useRef<AudioContext | null>(null);
// File system structure
const fileSystem: { home: FileSystemNode; var: FileSystemNode; etc: FileSystemNode; bin: FileSystemNode; tmp: FileSystemNode } = {
home: {
type: "dir",
children: {
guest: {
type: "dir",
children: {
"readme.txt": {
type: "file",
content: "ERROR 404: Page Not Found.\n\nSystem Integrity: 89%\nCheck /var/log for clues.\n\nTry: ls -la, cat .bash_history"
},
".bash_history": {
type: "file",
content: "ls -la\nwhoami\nfsociety\nexit"
},
"todo.txt": {
type: "file",
content: "- Fix the internet\n- Calculate the Ultimate Answer (try: 42)\n- Buy milk\n- Check Hawkins Lab logs"
},
"projects": {
type: "dir",
children: {
website: {
type: "dir",
children: {
"index.html": { type: "file", content: "<html><body>404</body></html>" }
}
}
}
}
}
}
}
},
var: {
type: "dir",
children: {
log: {
type: "dir",
children: {
syslog: {
type: "file",
content: "[ERR] Reality breach detected in HAWKINS_LAB sector.\n[WARN] Subject 011 has escaped containment.\n[ALERT] Dimensional gate unstable.\n[INFO] Try command: hawkins or 011"
},
"kern.log": {
type: "file",
content: "[ 0.000000] Linus Torvalds: 'This kernel is garbage.'\n[ 0.100000] Kernel tainted: M (Module has bad license)\n[ 0.200000] Torvalds: 'I'm not angry, just disappointed.'"
},
"auth.log": {
type: "file",
content: "Failed password for root from 127.0.0.1\nFailed password for elliot from 127.0.0.1"
}
}
}
}
},
etc: {
type: "dir",
children: {
passwd: {
type: "file",
content: "root:x:0:0:root:/root:/bin/bash\nguest:x:1000:1000:guest:/home/guest:/bin/bash\nelliot:x:509:509:mr_robot:/home/elliot:/bin/sh"
},
hosts: {
type: "file",
content: "127.0.0.1 localhost\n127.0.0.1 e-corp.com\n0.0.0.0 reality"
}
}
},
bin: {
type: "dir",
children: {
ls: { type: "exe" },
cat: { type: "exe" },
grep: { type: "exe" },
find: { type: "exe" }
}
},
tmp: {
type: "dir",
children: {}
}
};
const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
const printLine = (text: string, type?: string) => {
if (!outputRef.current) return;
const d = document.createElement('div');
d.innerHTML = text;
if (type === 'log-warn') d.style.color = '#ffb000';
if (type === 'log-err' || type === 'alert') d.style.color = '#ff3333';
if (type === 'log-dim') d.style.opacity = '0.6';
if (type === 'log-sys') d.style.color = 'cyan';
if (type === 'log-k') d.style.color = '#fff';
if (type === 'pulse-red') {
d.classList.add('pulse-red');
d.style.color = '#ff3333';
d.style.textShadow = '0 0 10px red';
}
outputRef.current.appendChild(d);
if (outputRef.current) {
outputRef.current.scrollTop = outputRef.current.scrollHeight;
}
};
const playSynth = (type: string) => {
try {
if (!audioCtxRef.current || systemFrozen) return;
const t = audioCtxRef.current.currentTime;
const osc = audioCtxRef.current.createOscillator();
const gain = audioCtxRef.current.createGain();
osc.connect(gain);
gain.connect(audioCtxRef.current.destination);
if (type === 'key') {
osc.type = 'square';
osc.frequency.setValueAtTime(600, t);
gain.gain.setValueAtTime(0.02, t);
gain.gain.exponentialRampToValueAtTime(0.001, t + 0.05);
osc.start();
osc.stop(t + 0.05);
} else if (type === 'beep') {
osc.type = 'sine';
osc.frequency.setValueAtTime(800, t);
gain.gain.setValueAtTime(0.1, t);
gain.gain.exponentialRampToValueAtTime(0.001, t + 0.2);
osc.start();
osc.stop(t + 0.2);
}
} catch {
// Ignore audio errors
}
};
const getCurrentDir = (): FileSystemNode => {
if (pathStr === "~" || pathStr.startsWith("~/")) {
return fileSystem.home.children!.guest;
} else if (pathStr.startsWith("/var/log")) {
// Return a wrapper node for /var/log directory
return {
type: 'dir',
children: fileSystem.var.children!.log.children
};
} else if (pathStr.startsWith("/var")) {
return fileSystem.var;
} else if (pathStr.startsWith("/etc")) {
return fileSystem.etc;
} else if (pathStr.startsWith("/bin")) {
return fileSystem.bin;
}
return currentPath || fileSystem.home.children!.guest;
};
const runCmd = async (cmdRaw: string) => {
if (systemFrozen || !outputRef.current) return;
printLine(`guest@404:${pathStr}$ ${cmdRaw}`, 'log-dim');
const args = cmdRaw.split(/\s+/);
const cmd = args[0].toLowerCase();
const newHistory = [...commandHistory, cmdRaw];
setCommandHistory(newHistory);
setHistoryIndex(newHistory.length);
await sleep(100);
const dir = getCurrentDir();
switch (cmd) {
case 'help':
printLine("--- SYSTEM UTILS ---", "log-sys");
printLine(" ls, cd, cat, grep, find, pwd, clear");
printLine(" whoami, uname, history, date, uptime");
printLine(" head, tail, wc, sort, uniq");
printLine("--- NETWORK ---", "log-sys");
printLine(" ping, hostname");
printLine("--- PROCESSES ---", "log-sys");
printLine(" ps, top, kill");
printLine("(Hints are hidden in the file system - try ls -la)");
break;
case 'ls':
const showHidden = args.includes('-a') || args.includes('-la') || args.includes('-l');
const longFormat = args.includes('-l') || args.includes('-la');
const items = Object.keys(dir.children || {})
.filter(n => !n.startsWith('.') || showHidden);
if (showHidden) {
items.unshift('..');
items.unshift('.');
}
items.sort((a, b) => {
if (a === '.') return -1;
if (b === '.') return 1;
if (a === '..') return -1;
if (b === '..') return 1;
const itemA = dir.children?.[a] as FileSystemNode | undefined;
const itemB = dir.children?.[b] as FileSystemNode | undefined;
if (!itemA || !itemB) return 0;
if (itemA.type === 'dir' && itemB.type !== 'dir') return -1;
if (itemA.type !== 'dir' && itemB.type === 'dir') return 1;
return a.localeCompare(b);
});
if (longFormat) {
printLine(`total ${items.length}`);
items.forEach(n => {
const item = dir.children?.[n];
if (!item && n !== '.' && n !== '..') return;
const isDir = item?.type === 'dir' || n === '.' || n === '..';
const perms = isDir ? 'drwxr-xr-x' : '-rw-r--r--';
const links = isDir ? '2' : '1';
const size = isDir ? '4096'.padStart(8) : (item?.content?.length || '0').toString().padStart(8);
const date = new Date();
const monthNames = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
const month = monthNames[date.getMonth()];
const day = date.getDate().toString().padStart(2);
const time = date.toTimeString().substring(0, 5);
const dateStr = `${month} ${day} ${time}`;
const filename = isDir ? `<span class='is-dir'>${n}/</span>` : n;
printLine(`${perms} ${links} guest guest ${size} ${dateStr} ${filename}`);
});
} else {
const formatted = items.map(n => {
const item = dir.children?.[n];
if (n === '.' || n === '..') return `<span class='is-dir'>${n}/</span>`;
if (item?.type === 'dir') return `<span class='is-dir'>${n}/</span>`;
if (item?.type === 'exe') return `<span class='is-exe'>${n}*</span>`;
return n;
});
printLine(formatted.join(' '));
}
break;
case 'cat':
const file = dir.children?.[args[1]] as FileSystemNode | undefined;
if (file && file.type === 'file') {
printLine(file.content || '');
} else {
printLine(`cat: ${args[1] || ''}: No such file`, 'log-err');
}
break;
case 'cd':
if (!args[1]) {
setCurrentPath(fileSystem.home.children!.guest);
setPathStr("~");
} else if (args[1] === '..') {
if (pathStr === "/var/log") {
setCurrentPath(fileSystem.var);
setPathStr("/var");
} else if (pathStr === "/var") {
setPathStr("/");
} else {
setCurrentPath(fileSystem.home.children!.guest);
setPathStr("~");
}
} else if (args[1] === '~' || args[1] === '/home/guest') {
setCurrentPath(fileSystem.home.children!.guest);
setPathStr("~");
} else if (args[1].startsWith('/var/log')) {
setCurrentPath({
type: 'dir',
children: fileSystem.var.children!.log.children
});
setPathStr("/var/log");
} else if (args[1].startsWith('/var')) {
setCurrentPath(fileSystem.var);
setPathStr("/var");
} else if (args[1].startsWith('/etc')) {
setCurrentPath(fileSystem.etc);
setPathStr("/etc");
} else {
const subdir = dir.children?.[args[1]] as FileSystemNode | undefined;
if (subdir && subdir.type === 'dir') {
setCurrentPath(subdir);
setPathStr(pathStr === "~" ? `~/${args[1]}` : `${pathStr}/${args[1]}`);
} else {
printLine(`cd: ${args[1]}: No such file or directory`, 'log-err');
}
}
break;
case 'pwd':
printLine(pathStr === "~" ? "/home/guest" : pathStr);
break;
case 'grep':
if (args.length < 3) {
printLine("Usage: grep [pattern] [file]");
} else {
const grepFile = dir.children?.[args[2]] as FileSystemNode | undefined;
if (grepFile && grepFile.type === 'file' && grepFile.content) {
const lines = grepFile.content.split('\n');
lines.forEach(l => {
if (l.toLowerCase().includes(args[1].toLowerCase())) {
printLine(l);
}
});
} else {
printLine(`grep: ${args[2]}: No such file`);
}
}
break;
case 'whoami':
printLine("guest");
break;
case 'uname':
if (args.includes('-a')) {
printLine("Linux 404-void 4.0.4-void #1 SMP PREEMPT Fri Jan 09 2025 x86_64 GNU/Linux");
} else {
printLine("Linux");
}
break;
case 'date':
printLine(new Date().toString());
break;
case 'uptime':
const uptime = Math.floor((Date.now() - performance.timing.navigationStart) / 1000);
const hours = Math.floor(uptime / 3600);
const mins = Math.floor((uptime % 3600) / 60);
const secs = uptime % 60;
printLine(`up ${hours}:${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}, 1 user, load average: 0.00, 0.00, 0.00`);
break;
case 'clear':
if (outputRef.current) outputRef.current.innerHTML = "";
break;
case 'history':
commandHistory.forEach((c, i) => printLine(` ${i + 1} ${c}`));
break;
case 'exit':
window.location.href = '/';
break;
// Easter eggs
case 'hawkins':
case '011':
case 'eleven':
case 'upsidedown':
if (hawkinsActive) {
printLine("Closing dimensional gate...", 'log-warn');
setHawkinsActive(false);
if (currentMusic) {
currentMusic.stop?.();
setCurrentMusic(null);
}
document.body.classList.remove('hawkins');
} else {
setHawkinsActive(true);
document.body.classList.add('hawkins');
printLine("Entering the Upside Down...", 'log-err');
}
break;
case 'fsociety':
case 'elliot':
case 'bonsoir':
if (fsocietyActive) {
setFsocietyActive(false);
setSystemFrozen(false);
if (inputContainerRef.current) inputContainerRef.current.style.display = 'flex';
if (currentMusic) {
currentMusic.stop?.();
setCurrentMusic(null);
}
document.body.classList.remove('fsociety-boot');
} else {
setFsocietyActive(true);
setSystemFrozen(true);
if (inputContainerRef.current) inputContainerRef.current.style.display = 'none';
if (outputRef.current) outputRef.current.innerHTML = "";
document.body.classList.add('fsociety-boot');
printLine("$ ./fsociety.sh", 'log-k');
await sleep(250);
printLine("[*] Initializing breach protocol...", 'log-warn');
await sleep(500);
printLine("Hello friend.", 'log-k');
await sleep(1000);
location.reload();
}
break;
case '42':
case 'answer':
printLine("Initializing Deep Thought...", 'log-sys');
await sleep(600);
printLine("Deep Thought: The Answer to the Ultimate Question of Life, the Universe, and Everything is...", 'log-k');
await sleep(1500);
printLine("42", 'log-k');
break;
case 'rm':
if (args[1] === '-rf' && args[2] === '/') {
setSystemFrozen(true);
if (inputContainerRef.current) inputContainerRef.current.style.display = 'none';
printLine("CRITICAL: Attempting to delete root filesystem...", 'log-err');
await sleep(500);
printLine("KERNEL PANIC: Unable to handle kernel paging request", 'pulse-red');
await sleep(2000);
location.reload();
} else if (args[1]) {
printLine("rm: cannot remove: Read-only file system", 'log-err');
} else {
printLine("Usage: rm [file]");
}
break;
default:
printLine(`bash: ${cmd}: command not found`, 'log-err');
}
if (inputRef.current && !systemFrozen) {
setTimeout(() => inputRef.current?.focus(), 50);
}
};
useEffect(() => {
setCurrentPath(fileSystem.home.children!.guest);
const initAudio = () => {
if (!audioCtxRef.current) {
try {
audioCtxRef.current = new (window.AudioContext || (window as { webkitAudioContext?: typeof AudioContext }).webkitAudioContext)();
} catch {
// Ignore
}
}
};
window.addEventListener('keydown', initAudio, { once: true });
const bootMessages = [
{ t: "[ 0.000000] Linux version 4.0.4-void (torvalds@kernel.org) (gcc version 9.4.0)", d: 200 },
{ t: "[ 0.050000] Command line: BOOT_IMAGE=/boot/vmlinuz-4.0.4 root=UUID=dead-beef ro quiet splash", d: 150 },
{ t: "[ 0.100000] x86/fpu: Supporting XSAVE feature 0x001: 'x87 floating point registers'", d: 100 },
{ t: "[ 0.150000] x86/fpu: Supporting XSAVE feature 0x002: 'SSE registers'", d: 100 },
{ t: "[ 0.200000] BIOS-provided physical RAM map:", d: 200 },
{ t: "[ 0.250000] BIOS-e820: [mem 0x0000000000000000-0x000000000009fbff] usable", d: 150 },
{ t: "[ 0.300000] BIOS-e820: [mem 0x000000000009fc00-0x000000000009ffff] reserved", d: 150 },
{ t: "[ 0.400000] Memory: 64K/1048576K available (404K kernel code, 404K rwdata, 404K rodata)", d: 300 },
{ t: "[ 0.600000] Calibrating delay loop... 800.00 BogoMIPS (lpj=400000)", d: 200 },
{ t: "[ 0.800000] Security Framework initialized", d: 200 },
{ t: "[ 1.000000] Tainted kernel: M (Module has bad license) P (Proprietary module loaded)", type: 'log-warn', d: 400 },
{ t: "[ 1.200000] Linus Torvalds: 'I'm not angry, I'm just disappointed in this boot process.'", d: 600 },
{ t: "[ 1.500000] Torvalds: 'This code is garbage. Who wrote this?'", d: 500 },
{ t: "[ 2.000000] [ OK ] Started udev Kernel Device Manager.", d: 200 },
{ t: "[ 2.200000] [ OK ] Mounted /dev/sda1 (Root Filesystem).", d: 200 },
{ t: "[ 2.400000] [TIME] Timed out waiting for device /dev/reality.", type: 'log-warn', d: 600 },
{ t: "[ 2.800000] [DEPEND] Dependency failed for Local File Systems.", type: 'log-err', d: 300 },
{ t: "[ 3.000000] [FAILED] Failed to start The Internet.", type: 'log-err', d: 400 },
{ t: "[ 3.200000] [FAILED] Failed to start Meaning of Life service.", type: 'log-err', d: 400 },
{ t: "[ 3.500000] Welcome to emergency mode. Type 'help' for available commands.", d: 200 },
];
const runBoot = async () => {
for (const msg of bootMessages) {
printLine(msg.t, msg.type);
await sleep(msg.d);
}
if (inputContainerRef.current) {
inputContainerRef.current.style.display = 'flex';
}
if (inputRef.current) {
setTimeout(() => inputRef.current?.focus(), 100);
}
};
if (outputRef.current) {
runBoot();
}
return () => {
if (currentMusic) {
currentMusic.stop?.();
}
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<>
<style dangerouslySetInnerHTML={{ __html: `
:root {
--bg-color: #020202;
--phosphor: #33ff00;
--phosphor-sec: #008f11;
--alert: #ff3333;
--font: 'Courier New', Courier, monospace;
}
body {
background-color: var(--bg-color) !important;
margin: 0;
height: 100vh;
overflow: hidden;
font-family: var(--font) !important;
color: var(--phosphor) !important;
user-select: none;
cursor: default;
transition: filter 0.5s, transform 0.5s;
}
.crt-wrap {
width: 100%;
height: 100vh;
padding: 30px;
box-sizing: border-box;
background: radial-gradient(circle at center, #111 0%, #000 100%);
position: relative;
}
.crt-wrap::before {
content: " ";
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(rgba(18, 16, 16, 0) 50%, rgba(0, 0, 0, 0.25) 50%),
linear-gradient(90deg, rgba(255, 0, 0, 0.06), rgba(0, 255, 0, 0.02), rgba(0, 0, 255, 0.06));
background-size: 100% 4px, 3px 100%;
pointer-events: none;
z-index: 90;
}
.glow {
text-shadow: 0 0 2px var(--phosphor-sec), 0 0 8px var(--phosphor);
}
#terminal {
height: 100%;
display: flex;
flex-direction: column;
max-width: 1400px;
margin: auto;
position: relative;
z-index: 10;
}
#output {
flex-grow: 1;
overflow-y: auto;
white-space: pre-wrap;
margin-bottom: 20px;
font-size: 1.1rem;
line-height: 1.4;
color: var(--phosphor);
}
#output::-webkit-scrollbar {
display: none;
}
.input-line {
display: none;
align-items: center;
font-size: 1.2rem;
border-top: 1px solid #333;
padding-top: 10px;
min-height: 30px;
}
.prompt {
margin-right: 10px;
font-weight: bold;
white-space: nowrap;
color: var(--phosphor);
}
#cmd-input {
background: transparent;
border: none;
color: inherit;
font-family: inherit;
font-size: inherit;
flex-grow: 1;
outline: none;
text-transform: lowercase;
caret-color: var(--phosphor);
}
body.hawkins {
--phosphor: #ff3333;
--phosphor-sec: #800000;
filter: contrast(1.6) sepia(1) hue-rotate(-30deg) saturate(4) brightness(0.6);
transform: rotate(180deg);
}
body.fsociety-boot {
background: #000;
filter: contrast(1.3) brightness(0.9);
}
body.meltdown {
animation: violent-shake 0.15s infinite, color-shift 0.8s infinite alternate;
}
@keyframes violent-shake {
0% { transform: translate(0, 0) rotate(0deg); }
25% { transform: translate(-8px, 5px) rotate(-1deg); }
50% { transform: translate(8px, -5px) rotate(1deg) skew(3deg); }
75% { transform: translate(-8px, -5px) rotate(-1deg); }
100% { transform: translate(8px, 5px) rotate(1deg); }
}
@keyframes color-shift {
0% { filter: hue-rotate(0deg) saturate(1); }
25% { filter: hue-rotate(90deg) saturate(2) brightness(1.2); }
50% { filter: hue-rotate(180deg) saturate(3) brightness(0.8); }
75% { filter: hue-rotate(270deg) saturate(2) brightness(1.1); }
100% { filter: hue-rotate(360deg) saturate(1); }
}
.pulse-red {
animation: pulse-red 1s infinite;
}
@keyframes pulse-red {
0%, 100% { color: var(--alert); text-shadow: 0 0 10px red; }
50% { color: #ff6666; text-shadow: 0 0 20px red, 0 0 30px red; }
}
.is-dir { color: #5e91ff; font-weight: bold; }
.is-exe { color: var(--phosphor); font-weight: bold; }
`}} />
<div className="crt-wrap glow">
<div id="terminal">
<div id="output" ref={outputRef}></div>
<div className="input-line" id="input-container" ref={inputContainerRef} style={{ display: 'none' }}>
<span className="prompt">guest@404:~$</span>
<input
type="text"
id="cmd-input"
ref={inputRef}
autoComplete="off"
spellCheck={false}
tabIndex={0}
onKeyDown={(e) => {
if (e.key === 'Enter') {
e.preventDefault();
const cmd = (e.target as HTMLInputElement).value.trim();
(e.target as HTMLInputElement).value = '';
runCmd(cmd);
} else if (e.key === 'ArrowUp' && historyIndex > 0) {
e.preventDefault();
const newIndex = historyIndex - 1;
setHistoryIndex(newIndex);
(e.target as HTMLInputElement).value = commandHistory[newIndex] || '';
} else if (e.key === 'ArrowDown' && historyIndex < commandHistory.length - 1) {
e.preventDefault();
const newIndex = historyIndex + 1;
setHistoryIndex(newIndex);
(e.target as HTMLInputElement).value = commandHistory[newIndex] || '';
} else if (e.key === 'ArrowDown') {
e.preventDefault();
setHistoryIndex(commandHistory.length);
(e.target as HTMLInputElement).value = '';
} else {
try {
playSynth('key');
} catch {
// Ignore audio errors
}
}
}}
/>
</div>
</div>
</div>
</>
);
}