-
+ {/* Command history */}
+
0}>
+
+
+ {(item) => (
+
+
+ freno@terminal
+ :
+ ~
+ $
+ {item.command}
+
+
+ {item.output}
+
+
+ )}
+
+
+
+
+ {/* Interactive input */}
+
{/* Footer */}
-
-
- System Error • Something went wrong
-
+
+ ERR | {" "}
+ Runtime Exception
{/* Custom styles */}
diff --git a/src/lib/terminal-commands.ts b/src/lib/terminal-commands.ts
new file mode 100644
index 0000000..09994c9
--- /dev/null
+++ b/src/lib/terminal-commands.ts
@@ -0,0 +1,275 @@
+export interface CommandHistoryItem {
+ command: string;
+ output: string;
+ type: "success" | "error" | "info";
+}
+
+export interface CommandContext {
+ navigate: (path: string) => void;
+ location: { pathname: string };
+ addToHistory: (
+ cmd: string,
+ output: string,
+ type: "success" | "error" | "info"
+ ) => void;
+ triggerCrash?: () => void;
+}
+
+export const createTerminalCommands = (context: CommandContext) => {
+ // Define available routes
+ const routes = [
+ { path: "/blog", name: "blog" },
+ { path: "/resume", name: "resume" },
+ { path: "/contact", name: "contact" },
+ { path: "/downloads", name: "downloads" },
+ { path: "/account", name: "account" }
+ ];
+
+ const commands: Record<
+ string,
+ { action: () => void; description: string; hidden?: boolean }
+ > = {
+ "cd ~": {
+ action: () => context.navigate("/"),
+ description: "Navigate to home"
+ },
+ "cd /": {
+ action: () => context.navigate("/"),
+ description: "Navigate to home"
+ },
+ "cd ..": {
+ action: () => window.history.back(),
+ description: "Go back"
+ },
+ ls: {
+ action: () => {
+ context.addToHistory(
+ "ls",
+ "home blog resume contact downloads login account",
+ "success"
+ );
+ },
+ description: "List available routes"
+ },
+ "ls -la": {
+ action: () => {
+ context.addToHistory(
+ "ls -la",
+ "drwxr-xr-x blog/\n-rw-r--r-- resume\n-rw-r--r-- contact\n-rw-r--r-- downloads\n-rw-r--r-- account",
+ "success"
+ );
+ },
+ description: "List available routes (detailed)"
+ },
+ pwd: {
+ action: () => {
+ context.addToHistory("pwd", context.location.pathname, "success");
+ },
+ description: "Print current path"
+ },
+ whoami: {
+ action: () => {
+ context.addToHistory("whoami", "guest", "success");
+ },
+ description: "Show current user"
+ },
+ help: {
+ action: () => {
+ const helpText = Object.entries(commands)
+ .filter(([, info]) => !info.hidden)
+ .map(([cmd, info]) => ` ${cmd.padEnd(20)} - ${info.description}`)
+ .join("\n");
+ context.addToHistory(
+ "help",
+ `Available commands:\n${helpText}`,
+ "info"
+ );
+ },
+ description: "Show this help message"
+ },
+ clear: {
+ action: () => {
+ context.addToHistory("clear", "", "info");
+ // Clear will be handled by the component
+ },
+ description: "Clear terminal history"
+ },
+ exit: {
+ action: () => context.navigate("/"),
+ description: "Exit (go home)"
+ },
+ crash: {
+ action: () => {
+ if (context.triggerCrash) {
+ context.triggerCrash();
+ } else {
+ throw new Error("💣");
+ }
+ },
+ description: "💣"
+ },
+ "sudo rm -rf /": {
+ action: () => {
+ context.addToHistory(
+ "sudo rm -rf /",
+ "Hey man... \nthat's like... not very cool.",
+ "error"
+ );
+ },
+ description: "Don't try this at home, kids"
+ },
+ cowsay: {
+ action: () => {
+ context.addToHistory(
+ "cowsay",
+ " ___________\n< You're lost! >\n -----------\n \\ ^__^\n \\ (oo)\\_______\n (__)\\ )\\/\\\n ||----w |\n || ||",
+ "success"
+ );
+ },
+ description: "Make the cow speak"
+ },
+ fortune: {
+ action: () => {
+ const fortunes = [
+ "The page you seek cannot be found, but fortune cookies remain plentiful.",
+ "A 404 error is just an opportunity to explore somewhere new.",
+ "You will find what you seek... just not here.",
+ "The path forward is unclear. Try going back.",
+ "Your lucky numbers are: 4, 0, 4",
+ "An error today keeps the bugs away... wait, that's not right.",
+ "In the land of the lost, the one with a map is king.",
+ "You will soon discover something that was always there."
+ ];
+ const fortune = fortunes[Math.floor(Math.random() * fortunes.length)];
+ context.addToHistory("fortune", fortune, "success");
+ },
+ description: "Get your fortune"
+ },
+ "echo $PATH": {
+ action: () => {
+ context.addToHistory(
+ "echo $PATH",
+ "/home:/blog:/resume:/contact:/downloads:/account",
+ "success"
+ );
+ },
+ description: "Show available paths"
+ },
+ neofetch: {
+ action: () => {
+ context.addToHistory(
+ "neofetch",
+ ` _,met$$$$$gg. guest@freno.dev\n ,g$$$$$$$$$$$$$$$P. ----------------\n ,g$$P\"\" \"\"\"Y$$.\" OS: 404 Not Found\n ,$$P' \`$$$. Shell: terminal-shell\n',$$P ,ggs. \`$$b: Resolution: Lost\n\`d$$' ,$P\"' . $$$ Theme: Catppuccin\n $$P d$' , $$P Terminal: web-terminal\n $$: $$. - ,d$$' CPU: Confusion (404)\n $$; Y$b._ _,d$P' Memory: ???\n Y$$. \`.\`\"Y$$$$P\"' \n \`$$b \"-.__ \n \`Y$$ \n \`Y$$. \n \`$$b. \n \`Y$$b. \n \`\"Y$b._ \n \`\"\"\"\" `,
+ "info"
+ );
+ },
+ description: "Display system info"
+ },
+ "cat /dev/urandom": {
+ action: () => {
+ const random = Array.from({ length: 10 }, () =>
+ Math.random().toString(36).substring(2, 15)
+ ).join("\n");
+ context.addToHistory(
+ "cat /dev/urandom",
+ random + "\n^C (interrupted)",
+ "success"
+ );
+ },
+ description: "Show random data"
+ },
+ uptime: {
+ action: () => {
+ context.addToHistory(
+ "uptime",
+ "up way too long, load average: ∞, ∞, ∞",
+ "success"
+ );
+ },
+ description: "Show system uptime"
+ },
+ date: {
+ action: () => {
+ context.addToHistory("date", new Date().toString(), "success");
+ },
+ description: "Display current date and time"
+ },
+ "uname -a": {
+ action: () => {
+ context.addToHistory(
+ "uname -a",
+ "ErrorOS 404.0.0 #1 SMP PREEMPT_DYNAMIC Web x86_64 GNU/Browser",
+ "success"
+ );
+ },
+ description: "Print system information"
+ }
+ };
+
+ // Add all cd variants for each route
+ routes.forEach((route) => {
+ // cd blog (visible in help)
+ commands[`cd ${route.name}`] = {
+ action: () => context.navigate(route.path),
+ description: `Navigate to ${route.name}`
+ };
+ // cd /blog (hidden alias)
+ commands[`cd ${route.path}`] = {
+ action: () => context.navigate(route.path),
+ description: `Navigate to ${route.name}`,
+ hidden: true
+ };
+ // cd ~/blog (hidden alias)
+ commands[`cd ~${route.path}`] = {
+ action: () => context.navigate(route.path),
+ description: `Navigate to ${route.name}`,
+ hidden: true
+ };
+ });
+
+ return commands;
+};
+
+export const executeTerminalCommand = (
+ cmd: string,
+ commands: Record<
+ string,
+ { action: () => void; description: string; hidden?: boolean }
+ >,
+ addToHistory: (
+ cmd: string,
+ output: string,
+ type: "success" | "error" | "info"
+ ) => void
+) => {
+ const trimmed = cmd.trim();
+
+ if (!trimmed) return;
+
+ if (commands[trimmed]) {
+ commands[trimmed].action();
+ } else if (trimmed.startsWith("cd ")) {
+ const path = trimmed.slice(3);
+ addToHistory(trimmed, `cd: ${path}: No such file or directory`, "error");
+ } else if (trimmed.startsWith("echo ")) {
+ const text = trimmed.slice(5);
+ addToHistory(trimmed, text, "success");
+ } else if (
+ trimmed.startsWith("cat ") ||
+ trimmed.startsWith("less ") ||
+ trimmed.startsWith("more ")
+ ) {
+ const file = trimmed.split(" ")[1];
+ addToHistory(
+ trimmed,
+ `${trimmed.split(" ")[0]}: ${file}: No such file or directory`,
+ "error"
+ );
+ } else {
+ addToHistory(
+ trimmed,
+ `command not found: ${trimmed}\nType 'help' for available commands`,
+ "error"
+ );
+ }
+};
diff --git a/src/routes/[...404].tsx b/src/routes/[...404].tsx
index 918dcdc..304477f 100644
--- a/src/routes/[...404].tsx
+++ b/src/routes/[...404].tsx
@@ -1,14 +1,90 @@
import { Title, Meta } from "@solidjs/meta";
import { HttpStatusCode } from "@solidjs/start";
-import { useNavigate } from "@solidjs/router";
-import { createEffect, createSignal, For } from "solid-js";
+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
([]);
+ const [historyIndex, setHistoryIndex] = createSignal(-1);
+ const [shouldCrash, setShouldCrash] = createSignal(false);
+ let inputRef: HTMLInputElement | undefined;
- createEffect(() => {
- const glitchChars = "!@#$%^&*()_+-=[]{}|;':\",./<>?~`";
+ 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(() => {
@@ -28,177 +104,193 @@ export default function NotFound() {
}
}, 300);
- return () => clearInterval(glitchInterval);
- });
+ inputRef?.focus();
- const createParticles = () => {
- return Array.from({ length: 50 }, (_, i) => ({
- id: i,
- left: `${Math.random() * 100}%`,
- top: `${Math.random() * 100}%`,
- animationDelay: `${Math.random() * 3}s`,
- animationDuration: `${2 + Math.random() * 3}s`
- }));
- };
+ onCleanup(() => {
+ clearInterval(glitchInterval);
+ });
+ });
return (
<>
+
+ {/*@ts-ignore (this is intentional)*/}
+
+
404 Not Found | Michael Freno
-
- {/* Animated particle background */}
-
-
- {(particle) => (
-
- )}
-
-
-
- {/* Animated grid background */}
-
+
inputRef?.focus()}
+ >
+ {/* Scanline effect */}
+
{/* Main content */}
-
- {/* Glitchy 404 */}
-
-
- {glitchText()}
-
-
+
+ {/* Terminal header */}
+
+
+ freno@terminal
+ :
+ ~
+ $
+
- {/* Error message with typewriter effect */}
-
-
- You seem to have drifted off into space...
-
-
- ...or the page you're looking for has drifted into the void.
-
- But don't worry, we can navigate you back to safety.
-
+ {/* 404 Error Display */}
+
+
+ error:
+ HTTP {glitchText()} - Not Found
+
+
+
+
+
✗
+
+
Failed to resolve route
+
+ The requested path does not exist in the routing table
+
+
+
+
+
+ → Location:{" "}
+ {location.pathname}
+
+
+
+
+
+ ℹ
+
+ Type help to see available
+ commands, or try one of the suggestions below
+
+
+
- {/* Action buttons */}
-
+ {/* Command suggestions */}
+
+
Quick commands:
+
navigate("/")}
- class="group relative overflow-hidden rounded-lg bg-gradient-to-r from-blue-600 to-purple-600 px-8 py-4 text-lg font-medium text-white shadow-lg transition-all duration-300 hover:scale-105 hover:shadow-xl hover:shadow-blue-500/25 active:scale-95"
+ 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"
>
-
-
- 🏠 Return Home
+ $
+ cd
+ ~
+
+ [Return home]
window.history.back()}
- class="group relative overflow-hidden rounded-lg border-2 border-slate-600 bg-transparent px-8 py-4 text-lg font-medium text-slate-300 transition-all duration-300 hover:border-blue-500 hover:bg-blue-500/10 hover:text-blue-400 active:scale-95"
+ 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"
>
- ← Go Back
+ $
+ cd
+ ..
+
+ [Go back]
+
+
+
+
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"
+ >
+ $
+ cd
+ ~/blog
+
+ [View blog]
+
- {/* Floating elements */}
-
-
-
-
+ {/* Command history */}
+
0}>
+
+
+ {(item) => (
+
+
+ freno@terminal
+ :
+ ~
+ $
+ {item.command}
+
+
+ {item.output}
+
+
+ )}
+
+
+
+
+ {/* Interactive input */}
+
{/* Footer */}
-
-
- Error Code: 404 • Page Not Found
-
+
+ 404 {" "}
+ | Page Not Found
{/* Custom styles */}
diff --git a/src/routes/error-test.tsx b/src/routes/error-test.tsx
new file mode 100644
index 0000000..f0b37e2
--- /dev/null
+++ b/src/routes/error-test.tsx
@@ -0,0 +1,27 @@
+import { createSignal, onMount } from "solid-js";
+
+export default function ErrorTest() {
+ const [shouldCrash, setShouldCrash] = createSignal(false);
+
+ // Crash on mount if flag is set
+ if (shouldCrash()) {
+ throw new Error("Test error - Error boundary triggered!");
+ }
+
+ return (
+
+
+
Error Boundary Test
+
+ Click the button below to trigger the error boundary
+
+
setShouldCrash(true)}
+ class="bg-red hover:bg-maroon rounded px-6 py-3 text-base font-bold transition"
+ >
+ 💥 Trigger Error Boundary
+
+
+
+ );
+}