clean up 401, simplify terminal error page
This commit is contained in:
@@ -1,7 +1,8 @@
|
|||||||
import { useNavigate } from "@solidjs/router";
|
import { useNavigate } from "@solidjs/router";
|
||||||
import { createSignal } from "solid-js";
|
import { createSignal, onCleanup, onMount } from "solid-js";
|
||||||
import { TerminalErrorPage } from "~/components/TerminalErrorPage";
|
import { TerminalErrorPage } from "~/components/TerminalErrorPage";
|
||||||
import { useDarkMode } from "~/context/darkMode";
|
import { useDarkMode } from "~/context/darkMode";
|
||||||
|
import { glitchText } from "~/lib/client-utils";
|
||||||
|
|
||||||
export interface ErrorBoundaryFallbackProps {
|
export interface ErrorBoundaryFallbackProps {
|
||||||
error: Error;
|
error: Error;
|
||||||
@@ -28,9 +29,15 @@ export default function ErrorBoundaryFallback(
|
|||||||
isDark = () => true;
|
isDark = () => true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const [glitchText, setGlitchText] = createSignal("ERROR");
|
const [glitchError, setGlitchError] = createSignal("ERROR");
|
||||||
|
|
||||||
console.error(props.error);
|
onMount(() => {
|
||||||
|
const interval = glitchText(glitchError(), setGlitchError);
|
||||||
|
|
||||||
|
onCleanup(() => {
|
||||||
|
clearInterval(interval);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
const errorContent = (
|
const errorContent = (
|
||||||
<div class="mb-8 w-full max-w-4xl font-mono">
|
<div class="mb-8 w-full max-w-4xl font-mono">
|
||||||
@@ -43,7 +50,7 @@ export default function ErrorBoundaryFallback(
|
|||||||
<div class="mb-2 flex items-start gap-2">
|
<div class="mb-2 flex items-start gap-2">
|
||||||
<span class="text-red text-xl">✗</span>
|
<span class="text-red text-xl">✗</span>
|
||||||
<div class="flex-1">
|
<div class="flex-1">
|
||||||
<div class="text-red mb-2 text-lg">{glitchText()}</div>
|
<div class="text-red mb-2 text-lg">{glitchError()}</div>
|
||||||
<div class="text-text">
|
<div class="text-text">
|
||||||
Application encountered an unexpected error
|
Application encountered an unexpected error
|
||||||
</div>
|
</div>
|
||||||
@@ -119,11 +126,6 @@ export default function ErrorBoundaryFallback(
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<TerminalErrorPage
|
<TerminalErrorPage
|
||||||
glitchText="ERROR"
|
|
||||||
glitchChars={"!@#$%^&*()_+-=[]{}|;':\",./<>?~`"}
|
|
||||||
glitchSpeed={150}
|
|
||||||
glitchThreshold={0.8}
|
|
||||||
glitchIntensity={0.6}
|
|
||||||
navigate={navigate!}
|
navigate={navigate!}
|
||||||
location={{
|
location={{
|
||||||
pathname: typeof window !== "undefined" ? window.location.pathname : "/"
|
pathname: typeof window !== "undefined" ? window.location.pathname : "/"
|
||||||
@@ -136,7 +138,6 @@ export default function ErrorBoundaryFallback(
|
|||||||
Runtime Exception
|
Runtime Exception
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
onGlitchTextChange={setGlitchText}
|
|
||||||
commandContext={{ isDark }}
|
commandContext={{ isDark }}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,12 +1,4 @@
|
|||||||
import {
|
import { createSignal, createEffect, onMount, For, Show, JSX } from "solid-js";
|
||||||
createSignal,
|
|
||||||
createEffect,
|
|
||||||
onMount,
|
|
||||||
onCleanup,
|
|
||||||
For,
|
|
||||||
Show,
|
|
||||||
JSX
|
|
||||||
} from "solid-js";
|
|
||||||
import {
|
import {
|
||||||
CommandHistoryItem,
|
CommandHistoryItem,
|
||||||
createTerminalCommands,
|
createTerminalCommands,
|
||||||
@@ -16,22 +8,16 @@ import {
|
|||||||
import { Btop } from "~/components/Btop";
|
import { Btop } from "~/components/Btop";
|
||||||
|
|
||||||
interface TerminalErrorPageProps {
|
interface TerminalErrorPageProps {
|
||||||
glitchText: string;
|
|
||||||
glitchChars: string;
|
|
||||||
glitchSpeed?: number;
|
|
||||||
glitchThreshold?: number;
|
|
||||||
glitchIntensity?: number;
|
|
||||||
navigate: (path: string) => void;
|
navigate: (path: string) => void;
|
||||||
location: { pathname: string };
|
location: { pathname: string };
|
||||||
errorContent: JSX.Element;
|
errorContent: JSX.Element;
|
||||||
quickActions: JSX.Element;
|
quickActions: JSX.Element;
|
||||||
footer: JSX.Element;
|
footer: JSX.Element;
|
||||||
onGlitchTextChange?: (text: string) => void;
|
|
||||||
commandContext?: Partial<CommandContext>;
|
commandContext?: Partial<CommandContext>;
|
||||||
|
disableTerminal?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function TerminalErrorPage(props: TerminalErrorPageProps) {
|
export function TerminalErrorPage(props: TerminalErrorPageProps) {
|
||||||
const [glitchText, setGlitchText] = createSignal(props.glitchText);
|
|
||||||
const [command, setCommand] = createSignal("");
|
const [command, setCommand] = createSignal("");
|
||||||
const [history, setHistory] = createSignal<CommandHistoryItem[]>([]);
|
const [history, setHistory] = createSignal<CommandHistoryItem[]>([]);
|
||||||
const [historyIndex, setHistoryIndex] = createSignal(-1);
|
const [historyIndex, setHistoryIndex] = createSignal(-1);
|
||||||
@@ -109,38 +95,7 @@ export function TerminalErrorPage(props: TerminalErrorPageProps) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
const originalText = props.glitchText;
|
|
||||||
const glitchChars = props.glitchChars;
|
|
||||||
const glitchSpeed = props.glitchSpeed || 300;
|
|
||||||
const glitchThreshold = props.glitchThreshold || 0.85;
|
|
||||||
const glitchIntensity = props.glitchIntensity || 0.7;
|
|
||||||
|
|
||||||
const glitchInterval = setInterval(() => {
|
|
||||||
if (Math.random() > glitchThreshold) {
|
|
||||||
let glitched = "";
|
|
||||||
for (let i = 0; i < originalText.length; i++) {
|
|
||||||
if (Math.random() > glitchIntensity) {
|
|
||||||
glitched +=
|
|
||||||
glitchChars[Math.floor(Math.random() * glitchChars.length)];
|
|
||||||
} else {
|
|
||||||
glitched += originalText[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setGlitchText(glitched);
|
|
||||||
props.onGlitchTextChange?.(glitched);
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
setGlitchText(originalText);
|
|
||||||
props.onGlitchTextChange?.(originalText);
|
|
||||||
}, 100);
|
|
||||||
}
|
|
||||||
}, glitchSpeed);
|
|
||||||
|
|
||||||
inputRef?.focus();
|
inputRef?.focus();
|
||||||
|
|
||||||
onCleanup(() => {
|
|
||||||
clearInterval(glitchInterval);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -198,8 +153,7 @@ export function TerminalErrorPage(props: TerminalErrorPageProps) {
|
|||||||
</For>
|
</For>
|
||||||
</div>
|
</div>
|
||||||
</Show>
|
</Show>
|
||||||
|
<Show when={!props.disableTerminal}>
|
||||||
{/* Interactive input */}
|
|
||||||
<div class="w-full max-w-4xl font-mono text-sm">
|
<div class="w-full max-w-4xl font-mono text-sm">
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<span class="text-green">freno@terminal</span>
|
<span class="text-green">freno@terminal</span>
|
||||||
@@ -219,6 +173,7 @@ export function TerminalErrorPage(props: TerminalErrorPageProps) {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</Show>
|
||||||
|
|
||||||
{/* Footer */}
|
{/* Footer */}
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -1,39 +1,113 @@
|
|||||||
import { PageHead } from "~/components/PageHead";
|
import { PageHead } from "~/components/PageHead";
|
||||||
import { HttpStatusCode } from "@solidjs/start";
|
import { HttpStatusCode } from "@solidjs/start";
|
||||||
import { useNavigate } from "@solidjs/router";
|
import { useLocation, useNavigate } from "@solidjs/router";
|
||||||
import { createEffect, createSignal, For, onCleanup } from "solid-js";
|
import { createSignal, onMount, onCleanup } from "solid-js";
|
||||||
import { ERROR_PAGE_CONFIG } from "~/config";
|
|
||||||
import { glitchText } from "~/lib/client-utils";
|
import { glitchText } from "~/lib/client-utils";
|
||||||
|
import { TerminalErrorPage } from "~/components/TerminalErrorPage";
|
||||||
|
import { useDarkMode } from "~/context/darkMode";
|
||||||
|
|
||||||
export default function Page_401() {
|
export default function Page_401() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [glitchText, setGlitchText] = createSignal("401");
|
const [glitch401, setGlitch401] = createSignal("401");
|
||||||
|
|
||||||
createEffect(() => {
|
const location = useLocation();
|
||||||
const interval = glitchText(
|
|
||||||
"401",
|
|
||||||
setGlitchText,
|
|
||||||
ERROR_PAGE_CONFIG.GLITCH_INTERVAL_MS,
|
|
||||||
ERROR_PAGE_CONFIG.GLITCH_DURATION_MS
|
|
||||||
);
|
|
||||||
|
|
||||||
onCleanup(() => clearInterval(interval));
|
onMount(() => {
|
||||||
|
const interval = glitchText(glitch401(), setGlitch401);
|
||||||
|
|
||||||
|
onCleanup(() => {
|
||||||
|
clearInterval(interval);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const createParticles = () => {
|
const errorContent = (
|
||||||
return Array.from({ length: ERROR_PAGE_CONFIG.PARTICLE_COUNT }, (_, i) => ({
|
<div class="mb-8 w-full max-w-4xl font-mono">
|
||||||
id: i,
|
<div class="mb-4 flex items-center gap-2">
|
||||||
left: `${Math.random() * 100}%`,
|
<span class="text-red text-3xl font-bold">{glitch401()}</span>
|
||||||
top: `${Math.random() * 100}%`,
|
<div class="border-overlay0 h-8 border-l" />
|
||||||
animationDelay: `${Math.random() * 3}s`,
|
<div class="flex flex-col">
|
||||||
animationDuration: `${2 + Math.random() * 3}s`
|
<span class="text-red font-mono text-sm">error:</span>
|
||||||
}));
|
<span class="text-text font-mono">Unauthorized</span>
|
||||||
};
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
function doubleBack() {
|
<div class="border-red bg-mantle mb-6 border-l-4 p-4 text-sm">
|
||||||
window.history.go(-2);
|
<div class="mb-2 flex items-start gap-2">
|
||||||
|
<span class="text-red">✗</span>
|
||||||
|
<div class="flex-1">
|
||||||
|
<div class="text-text">Access Denied</div>
|
||||||
|
<div class="text-subtext0 mt-1">
|
||||||
|
Authentication required to access this resource
|
||||||
|
</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>
|
||||||
|
);
|
||||||
|
const quickActions = (
|
||||||
|
<div class="mb-8 w-full max-w-4xl space-y-3 font-mono text-sm">
|
||||||
|
<div class="text-subtext1">Quick actions:</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
onClick={() => navigate("/login")}
|
||||||
|
class="group border-surface0 bg-mantle hover:border-yellow 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-yellow group-hover:text-peach">./authenticate</span>
|
||||||
|
<span class="text-subtext1">--login</span>
|
||||||
|
<span class="text-subtext1 ml-auto opacity-0 transition-opacity group-hover:opacity-100">
|
||||||
|
[Login]
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<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>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
let isDark: () => boolean;
|
||||||
|
try {
|
||||||
|
const darkMode = useDarkMode();
|
||||||
|
isDark = darkMode.isDark;
|
||||||
|
} catch (e) {
|
||||||
|
isDark = () => true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<PageHead
|
<PageHead
|
||||||
@@ -41,183 +115,20 @@ export default function Page_401() {
|
|||||||
description="401 - Unauthorized access. Please log in to access this page."
|
description="401 - Unauthorized access. Please log in to access this page."
|
||||||
/>
|
/>
|
||||||
<HttpStatusCode code={401} />
|
<HttpStatusCode code={401} />
|
||||||
<div class="relative min-h-screen w-full overflow-hidden bg-gradient-to-br from-slate-900 via-amber-950/20 to-slate-900 dark:from-black dark:via-amber-950/30 dark:to-black">
|
<TerminalErrorPage
|
||||||
{/* Animated particle background */}
|
navigate={navigate}
|
||||||
<div class="absolute inset-0 overflow-hidden">
|
location={location}
|
||||||
<For each={createParticles()}>
|
errorContent={errorContent}
|
||||||
{(particle) => (
|
quickActions={quickActions}
|
||||||
<div
|
disableTerminal
|
||||||
class="absolute animate-pulse"
|
footer={
|
||||||
style={{
|
<>
|
||||||
left: particle.left,
|
<span class="text-red">401</span>{" "}
|
||||||
top: particle.top,
|
<span class="text-subtext0">|</span> Unauthorized Access
|
||||||
"animation-delay": particle.animationDelay,
|
</>
|
||||||
"animation-duration": particle.animationDuration
|
}
|
||||||
}}
|
commandContext={{ isDark }}
|
||||||
>
|
|
||||||
<div class="h-1 w-1 rounded-full bg-amber-400 opacity-30 dark:bg-amber-300" />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</For>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Animated grid background */}
|
|
||||||
<div class="absolute inset-0 opacity-10">
|
|
||||||
<div
|
|
||||||
class="h-full w-full"
|
|
||||||
style={{
|
|
||||||
"background-image": `
|
|
||||||
linear-gradient(rgba(251, 191, 36, 0.3) 1px, transparent 1px),
|
|
||||||
linear-gradient(90deg, rgba(251, 191, 36, 0.3) 1px, transparent 1px)
|
|
||||||
`,
|
|
||||||
"background-size": "50px 50px",
|
|
||||||
animation: "grid-move 20s linear infinite"
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Logo overlay */}
|
|
||||||
<div class="absolute inset-0 flex items-center justify-center opacity-10">
|
|
||||||
<picture class="h-80 object-cover sm:h-96">
|
|
||||||
<source
|
|
||||||
srcSet="/WhiteLogo.png"
|
|
||||||
media="(prefers-color-scheme: dark)"
|
|
||||||
/>
|
|
||||||
<img
|
|
||||||
src="/BlackLogo.png"
|
|
||||||
alt="logo"
|
|
||||||
class="mx-auto brightness-50"
|
|
||||||
/>
|
|
||||||
</picture>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Main content */}
|
|
||||||
<div class="relative z-10 flex min-h-screen flex-col items-center justify-center px-4 text-center">
|
|
||||||
{/* Glitchy 401 */}
|
|
||||||
<div class="mb-8">
|
|
||||||
<h1
|
|
||||||
class="bg-gradient-to-r from-amber-400 via-orange-500 to-amber-600 bg-clip-text text-8xl font-bold text-transparent select-none md:text-9xl"
|
|
||||||
style={{
|
|
||||||
"text-shadow": "0 0 30px rgba(251, 191, 36, 0.5)",
|
|
||||||
filter: "drop-shadow(0 0 10px rgba(251, 191, 36, 0.3))"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{glitchText()}
|
|
||||||
</h1>
|
|
||||||
<div class="mx-auto mt-2 h-1 w-32 animate-pulse bg-gradient-to-r from-transparent via-amber-500 to-transparent" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Error message */}
|
|
||||||
<div class="max-w-2xl space-y-4">
|
|
||||||
<h2 class="animate-fade-in text-2xl font-light text-slate-300 md:text-3xl dark:text-slate-400">
|
|
||||||
Access Denied
|
|
||||||
</h2>
|
|
||||||
<p class="animate-fade-in-delay text-lg text-slate-400 dark:text-slate-500">
|
|
||||||
You lack authentication sufficient for that page.
|
|
||||||
<br />
|
|
||||||
Please log in or return to a safe location.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Action buttons */}
|
|
||||||
<div class="mt-12 flex flex-col gap-4 sm:flex-row">
|
|
||||||
<button
|
|
||||||
onClick={() => navigate("/login")}
|
|
||||||
class="group relative overflow-hidden rounded-lg bg-gradient-to-r from-amber-600 to-orange-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-amber-500/25 active:scale-95"
|
|
||||||
>
|
|
||||||
<div class="absolute inset-0 bg-gradient-to-r from-amber-700 to-orange-700 opacity-0 transition-opacity duration-300 group-hover:opacity-100" />
|
|
||||||
<span class="relative flex items-center gap-2">🔐 Login</span>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button
|
|
||||||
onClick={() => 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"
|
|
||||||
>
|
|
||||||
<div class="absolute inset-0 bg-gradient-to-r from-blue-700 to-purple-700 opacity-0 transition-opacity duration-300 group-hover:opacity-100" />
|
|
||||||
<span class="relative flex items-center gap-2">
|
|
||||||
🏠 Return Home
|
|
||||||
</span>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button
|
|
||||||
onClick={doubleBack}
|
|
||||||
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-amber-500 hover:bg-amber-500/10 hover:text-amber-400 active:scale-95"
|
|
||||||
>
|
|
||||||
<span class="relative flex items-center gap-2">← Go Back</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Floating elements */}
|
|
||||||
<div class="animate-bounce-slow absolute top-20 left-10">
|
|
||||||
<div class="h-6 w-6 rotate-45 bg-gradient-to-br from-amber-400 to-orange-500 opacity-60" />
|
|
||||||
</div>
|
|
||||||
<div class="animate-bounce-slow-delay absolute top-32 right-16">
|
|
||||||
<div class="h-4 w-4 rounded-full bg-gradient-to-br from-orange-400 to-amber-500 opacity-60" />
|
|
||||||
</div>
|
|
||||||
<div class="animate-bounce-slow absolute bottom-20 left-20">
|
|
||||||
<div
|
|
||||||
class="h-5 w-5 bg-gradient-to-br from-amber-500 to-orange-400 opacity-60"
|
|
||||||
style={{ "clip-path": "polygon(50% 0%, 0% 100%, 100% 100%)" }}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Footer */}
|
|
||||||
<div class="absolute bottom-8 left-1/2 -translate-x-1/2">
|
|
||||||
<p class="text-sm text-slate-500 dark:text-slate-600">
|
|
||||||
Error Code: 401 • Unauthorized Access
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Custom styles */}
|
|
||||||
<style>{`
|
|
||||||
@keyframes grid-move {
|
|
||||||
0% {
|
|
||||||
transform: translate(0, 0);
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
transform: translate(50px, 50px);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.animate-fade-in {
|
|
||||||
animation: fadeIn 1s ease-out 0.5s both;
|
|
||||||
}
|
|
||||||
|
|
||||||
.animate-fade-in-delay {
|
|
||||||
animation: fadeIn 1s ease-out 1s both;
|
|
||||||
}
|
|
||||||
|
|
||||||
.animate-bounce-slow {
|
|
||||||
animation: bounce-slow 3s ease-in-out infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
.animate-bounce-slow-delay {
|
|
||||||
animation: bounce-slow 3s ease-in-out infinite 1.5s;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes fadeIn {
|
|
||||||
from {
|
|
||||||
opacity: 0;
|
|
||||||
transform: translateY(20px);
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
opacity: 1;
|
|
||||||
transform: translateY(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes bounce-slow {
|
|
||||||
0%,
|
|
||||||
100% {
|
|
||||||
transform: translateY(0) rotate(0deg);
|
|
||||||
}
|
|
||||||
50% {
|
|
||||||
transform: translateY(-20px) rotate(180deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`}</style>
|
|
||||||
</div>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import { PageHead } from "~/components/PageHead";
|
import { PageHead } from "~/components/PageHead";
|
||||||
import { HttpStatusCode } from "@solidjs/start";
|
import { HttpStatusCode } from "@solidjs/start";
|
||||||
import { useNavigate, useLocation } from "@solidjs/router";
|
import { useNavigate, useLocation } from "@solidjs/router";
|
||||||
import { createSignal, Show } from "solid-js";
|
import { createSignal, onCleanup, onMount, Show } from "solid-js";
|
||||||
import { TerminalErrorPage } from "~/components/TerminalErrorPage";
|
import { TerminalErrorPage } from "~/components/TerminalErrorPage";
|
||||||
import { useDarkMode } from "~/context/darkMode";
|
import { useDarkMode } from "~/context/darkMode";
|
||||||
|
import { glitchText } from "~/lib/client-utils";
|
||||||
|
|
||||||
// Component that crashes when rendered
|
// Component that crashes when rendered
|
||||||
function CrashComponent() {
|
function CrashComponent() {
|
||||||
@@ -14,14 +15,26 @@ export default function NotFound() {
|
|||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const { isDark } = useDarkMode();
|
const { isDark } = useDarkMode();
|
||||||
const [glitchText, setGlitchText] = createSignal("404");
|
const [glitch404, setGlitch404] = createSignal("404");
|
||||||
const [shouldCrash, setShouldCrash] = createSignal(false);
|
const [shouldCrash, setShouldCrash] = createSignal(false);
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
const interval = glitchText(glitch404(), setGlitch404);
|
||||||
|
|
||||||
|
onCleanup(() => {
|
||||||
|
clearInterval(interval);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
const errorContent = (
|
const errorContent = (
|
||||||
<div class="mb-8 w-full max-w-4xl font-mono">
|
<div class="mb-8 w-full max-w-4xl font-mono">
|
||||||
<div class="mb-4 flex items-center gap-2">
|
<div class="mb-4 flex items-center gap-2">
|
||||||
<span class="text-red">error:</span>
|
<span class="text-red text-3xl font-bold">{glitch404()}</span>
|
||||||
<span class="text-text">HTTP {glitchText()} - Not Found</span>
|
<div class="border-overlay0 h-8 border-l" />
|
||||||
|
<div class="flex flex-col">
|
||||||
|
<span class="text-red font-mono text-sm">error:</span>
|
||||||
|
<span class="text-text font-mono">Not Found</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="border-red bg-mantle mb-6 border-l-4 p-4 text-sm">
|
<div class="border-red bg-mantle mb-6 border-l-4 p-4 text-sm">
|
||||||
@@ -107,11 +120,6 @@ export default function NotFound() {
|
|||||||
/>
|
/>
|
||||||
<HttpStatusCode code={404} />
|
<HttpStatusCode code={404} />
|
||||||
<TerminalErrorPage
|
<TerminalErrorPage
|
||||||
glitchText="404"
|
|
||||||
glitchChars={"!@#$%^&*()_+-=[]{}|;':\",./<>?~`0123456789"}
|
|
||||||
glitchSpeed={150}
|
|
||||||
glitchThreshold={0.85}
|
|
||||||
glitchIntensity={0.7}
|
|
||||||
navigate={navigate}
|
navigate={navigate}
|
||||||
location={location}
|
location={location}
|
||||||
errorContent={errorContent}
|
errorContent={errorContent}
|
||||||
@@ -122,7 +130,6 @@ export default function NotFound() {
|
|||||||
<span class="text-subtext0">|</span> Page Not Found
|
<span class="text-subtext0">|</span> Page Not Found
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
onGlitchTextChange={setGlitchText}
|
|
||||||
commandContext={{
|
commandContext={{
|
||||||
triggerCrash: () => setShouldCrash(true),
|
triggerCrash: () => setShouldCrash(true),
|
||||||
isDark: isDark
|
isDark: isDark
|
||||||
|
|||||||
Reference in New Issue
Block a user