Files
freno-dev/src/routes/[...404].tsx
2025-12-31 12:00:44 -05:00

301 lines
11 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { Title, Meta } from "@solidjs/meta";
import { HttpStatusCode } from "@solidjs/start";
import { useNavigate, useLocation } from "@solidjs/router";
import { createSignal, onCleanup, onMount, For, Show } from "solid-js";
import {
CommandHistoryItem,
createTerminalCommands,
executeTerminalCommand
} from "~/lib/terminal-commands";
// Component that crashes when rendered
function CrashComponent() {
throw new Error("Terminal crash test - triggering error boundary");
}
export default function NotFound() {
const navigate = useNavigate();
const location = useLocation();
const [glitchText, setGlitchText] = createSignal("404");
const [command, setCommand] = createSignal("");
const [history, setHistory] = createSignal<CommandHistoryItem[]>([]);
const [historyIndex, setHistoryIndex] = createSignal(-1);
const [shouldCrash, setShouldCrash] = createSignal(false);
let inputRef: HTMLInputElement | undefined;
const addToHistory = (
cmd: string,
output: string,
type: "success" | "error" | "info"
) => {
if (cmd === "clear") {
setHistory([]);
} else {
setHistory([...history(), { command: cmd, output, type }]);
}
};
const commands = createTerminalCommands({
navigate,
location,
addToHistory,
triggerCrash: () => setShouldCrash(true)
});
const handleKeyDown = (e: KeyboardEvent) => {
if (e.key === "Enter") {
executeTerminalCommand(command(), commands, addToHistory);
setCommand("");
setHistoryIndex(-1);
} else if (e.key === "ArrowUp") {
e.preventDefault();
const allCommands = history().map((h) => h.command);
if (allCommands.length > 0) {
const newIndex =
historyIndex() === -1
? allCommands.length - 1
: Math.max(0, historyIndex() - 1);
setHistoryIndex(newIndex);
setCommand(allCommands[newIndex]);
}
} else if (e.key === "ArrowDown") {
e.preventDefault();
const allCommands = history().map((h) => h.command);
if (historyIndex() !== -1) {
const newIndex = Math.min(allCommands.length - 1, historyIndex() + 1);
setHistoryIndex(newIndex);
setCommand(allCommands[newIndex]);
}
} else if (e.key === "Tab") {
e.preventDefault();
const typed = command().toLowerCase();
const matches = Object.keys(commands).filter((cmd) =>
cmd.startsWith(typed)
);
if (matches.length === 1) {
setCommand(matches[0]);
} else if (matches.length > 1) {
addToHistory(command(), matches.join(" "), "info");
}
} else if (e.key === "l" && e.ctrlKey) {
e.preventDefault();
setHistory([]);
}
};
onMount(() => {
const glitchChars = "!@#$%^&*()_+-=[]{}|;':\",./<>?~`0123456789";
const originalText = "404";
const glitchInterval = setInterval(() => {
if (Math.random() > 0.85) {
let glitched = "";
for (let i = 0; i < originalText.length; i++) {
if (Math.random() > 0.7) {
glitched +=
glitchChars[Math.floor(Math.random() * glitchChars.length)];
} else {
glitched += originalText[i];
}
}
setGlitchText(glitched);
setTimeout(() => setGlitchText(originalText), 100);
}
}, 300);
inputRef?.focus();
onCleanup(() => {
clearInterval(glitchInterval);
});
});
return (
<>
<Show when={shouldCrash()}>
{/*@ts-ignore (this is intentional)*/}
<CrashComponent />
</Show>
<Title>404 Not Found | Michael Freno</Title>
<Meta
name="description"
content="404 - Page not found. The page you're looking for doesn't exist."
/>
<HttpStatusCode code={404} />
<div
class="relative min-h-screen w-full overflow-hidden"
onClick={() => inputRef?.focus()}
>
{/* Scanline effect */}
<div class="pointer-events-none absolute inset-0 z-20 opacity-5">
<div
class="h-full w-full"
style={{
"background-image":
"repeating-linear-gradient(0deg, transparent, transparent 2px, rgba(0,0,0,0.2) 2px, rgba(0,0,0,0.2) 4px)",
animation: "scanline 8s linear infinite"
}}
/>
</div>
{/* Main content */}
<div class="relative z-10 flex min-h-screen flex-col items-start justify-start px-8 py-16 md:px-16">
{/* Terminal header */}
<div class="mb-8 w-full max-w-4xl">
<div class="border-surface0 text-subtext0 flex items-center gap-2 border-b pb-2 font-mono text-sm">
<span class="text-green">freno@terminal</span>
<span class="text-subtext1">:</span>
<span class="text-blue">~</span>
<span class="text-subtext1">$</span>
</div>
</div>
{/* 404 Error Display */}
<div class="mb-8 w-full max-w-4xl font-mono">
<div class="mb-4 flex items-center gap-2">
<span class="text-red">error:</span>
<span class="text-text">HTTP {glitchText()} - Not Found</span>
</div>
<div class="border-red bg-mantle mb-6 border-l-4 p-4 text-sm">
<div class="mb-2 flex items-start gap-2">
<span class="text-red">✗</span>
<div class="flex-1">
<div class="text-text">Failed to resolve route</div>
<div class="text-subtext0 mt-1">
The requested path does not exist in the routing table
</div>
</div>
</div>
<div class="text-subtext1 mt-3">
<span class="text-yellow">→</span> Location:{" "}
<span class="text-peach">{location.pathname}</span>
</div>
</div>
<div class="text-subtext0 space-y-2 text-sm">
<div class="flex items-start gap-2">
<span class="text-blue">ℹ</span>
<span>
Type <span class="text-green">help</span> to see available
commands, or try one of the suggestions below
</span>
</div>
</div>
</div>
{/* Command suggestions */}
<div class="mb-8 w-full max-w-4xl space-y-3 font-mono text-sm">
<div class="text-subtext1">Quick commands:</div>
<button
onClick={() => navigate("/")}
class="group border-surface0 bg-mantle hover:border-blue hover:bg-surface0 flex w-full items-center gap-2 border px-4 py-3 text-left transition-all"
>
<span class="text-green">$</span>
<span class="text-blue group-hover:text-sky">cd</span>
<span class="text-text group-hover:text-blue">~</span>
<span class="text-subtext1 ml-auto opacity-0 transition-opacity group-hover:opacity-100">
[Return home]
</span>
</button>
<button
onClick={() => window.history.back()}
class="group border-surface0 bg-mantle hover:border-blue hover:bg-surface0 flex w-full items-center gap-2 border px-4 py-3 text-left transition-all"
>
<span class="text-green">$</span>
<span class="text-blue group-hover:text-sky">cd</span>
<span class="text-text group-hover:text-blue">..</span>
<span class="text-subtext1 ml-auto opacity-0 transition-opacity group-hover:opacity-100">
[Go back]
</span>
</button>
<button
onClick={() => navigate("/blog")}
class="group border-surface0 bg-mantle hover:border-blue hover:bg-surface0 flex w-full items-center gap-2 border px-4 py-3 text-left transition-all"
>
<span class="text-green">$</span>
<span class="text-blue group-hover:text-sky">cd</span>
<span class="text-text group-hover:text-blue">~/blog</span>
<span class="text-subtext1 ml-auto opacity-0 transition-opacity group-hover:opacity-100">
[View blog]
</span>
</button>
</div>
{/* Command history */}
<Show when={history().length > 0}>
<div class="mb-4 w-full max-w-4xl font-mono text-sm">
<For each={history()}>
{(item) => (
<div class="mb-3">
<div class="text-subtext0 flex items-center gap-2">
<span class="text-green">freno@terminal</span>
<span class="text-subtext1">:</span>
<span class="text-blue">~</span>
<span class="text-subtext1">$</span>
<span class="text-text">{item.command}</span>
</div>
<div
class="mt-1 whitespace-pre-wrap"
classList={{
"text-text": item.type === "success",
"text-red": item.type === "error",
"text-blue": item.type === "info"
}}
>
{item.output}
</div>
</div>
)}
</For>
</div>
</Show>
{/* Interactive input */}
<div class="w-full max-w-4xl font-mono text-sm">
<div class="flex items-center gap-2">
<span class="text-green">freno@terminal</span>
<span class="text-subtext1">:</span>
<span class="text-blue">~</span>
<span class="text-subtext1">$</span>
<input
ref={inputRef}
type="text"
value={command()}
onInput={(e) => setCommand(e.currentTarget.value)}
onKeyDown={handleKeyDown}
class="text-text caret-text ml-1 flex-1 border-none bg-transparent outline-none"
autocomplete="off"
spellcheck={false}
/>
</div>
</div>
{/* Footer */}
<div class="text-subtext1 absolute right-4 bottom-4 font-mono text-xs">
<span class="text-red">404</span>{" "}
<span class="text-subtext0">|</span> Page Not Found
</div>
</div>
{/* Custom styles */}
<style>{`
@keyframes scanline {
0% {
transform: translateY(-100%);
}
100% {
transform: translateY(100%);
}
}
`}</style>
</div>
</>
);
}