update not found page
This commit is contained in:
parent
b3c03e0c41
commit
f8195aa401
BIN
public/images/coralz_0.png
Normal file
BIN
public/images/coralz_0.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 177 KiB |
BIN
public/images/coralz_1.png
Normal file
BIN
public/images/coralz_1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 176 KiB |
BIN
public/sound/poke.wav
Normal file
BIN
public/sound/poke.wav
Normal file
Binary file not shown.
BIN
public/sound/squeak.wav
Normal file
BIN
public/sound/squeak.wav
Normal file
Binary file not shown.
@ -7,6 +7,17 @@
|
||||
--primary: #F48120;
|
||||
}
|
||||
|
||||
@keyframes click-bounce {
|
||||
0%, 100% {
|
||||
transform: scale(1, 1);
|
||||
/* animation-timing-function: linear(0.2, 0.8, 1); */
|
||||
}
|
||||
30% {
|
||||
transform: scale(1.18, 0.9);
|
||||
/* animation-timing-function: cubic-bezier(0.8, 0, 1, 1); */
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes silly-bounce {
|
||||
0%, 100% {
|
||||
transform: translate(0px, 0px) scale(0.9, 1.1);
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
import WobblingImage from "@/components/wobbling-image";
|
||||
import type { Metadata } from "next";
|
||||
import Link from "@/components/link";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Wep wep",
|
||||
@ -8,12 +10,19 @@ export default function NotFound() {
|
||||
return (
|
||||
<main className="flex items-center h-screen pt-16 md:pt-32 pb-12 px-8 md:px-0">
|
||||
<div className="mx-auto w-[380px]">
|
||||
<header className="mb-8 -ms-5 text-center">
|
||||
<h1 className="font-bold text-8xl">404</h1>
|
||||
</header>
|
||||
<div className="">
|
||||
Oh you lost, you know there's nothing here...
|
||||
</div>
|
||||
<noscript>
|
||||
<header className="mb-8 -ms-5 text-center">
|
||||
<h1 className="font-bold text-8xl">404</h1>
|
||||
</header>
|
||||
<div className="">
|
||||
Oh you lost, you know there's nothing here...
|
||||
</div>
|
||||
</noscript>
|
||||
<WobblingImage
|
||||
img1="/images/coralz_0.png"
|
||||
img2="/images/coralz_1.png"
|
||||
/>
|
||||
<Link target="_blank" href="https://x.com/JoelGuerraC/status/1840178999546319101">Source</Link>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
|
||||
@ -3,9 +3,8 @@ import { NolaGlitchClientOnly } from "@/components/nola-glitch";
|
||||
import { Sosmed } from "@/components/sosmed";
|
||||
import HomeText from "@/components/home-text.mdx"
|
||||
|
||||
import Link from "next/link";
|
||||
import Link from "@/components/link";
|
||||
import { FakeWindow, HomeWindows } from "@/components/windows";
|
||||
import Taskbar from "@/components/taskbar";
|
||||
import { WindowManagerProvider } from "@/hooks/window-manager";
|
||||
|
||||
export default function Home() {
|
||||
@ -28,12 +27,12 @@ export default function Home() {
|
||||
<HomeText />
|
||||
</article>
|
||||
<section className="my-8">
|
||||
<p>⚡ Powered with <Link href="https://www.cloudflare.com/" target="_blank" className="text-primary underline">Cloudflare</Link> ☁️</p>
|
||||
<p>⚡ Powered with <Link href="https://www.cloudflare.com/" target="_blank">Cloudflare</Link> ☁️</p>
|
||||
</section>
|
||||
<footer className="mt-20 text-center">
|
||||
<p>© <span className="text-sm">2025 Nomi Nonszy</span></p>
|
||||
<p className="text-sm">
|
||||
<Link href={"/terms"} className="text-primary underline">Terms</Link> and <Link href={"/privacy"} className="text-primary underline">Privacy</Link>
|
||||
<Link href={"/terms"}>Terms</Link> and <Link href={"/privacy"}>Privacy</Link>
|
||||
</p>
|
||||
</footer>
|
||||
</FakeWindow>
|
||||
|
||||
14
src/components/link.tsx
Normal file
14
src/components/link.tsx
Normal file
@ -0,0 +1,14 @@
|
||||
import clsx from "clsx";
|
||||
import NextLink from "next/link"
|
||||
|
||||
function Link({ className, ...props }: React.ComponentProps<typeof NextLink>) {
|
||||
return (
|
||||
<NextLink
|
||||
className={clsx("text-primary underline", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
export default Link;
|
||||
@ -15,7 +15,7 @@ export const SimpleArticle = ({
|
||||
Back
|
||||
</ButtonPrimary>
|
||||
</Link>
|
||||
<article className="space-y-5 [&_p]:leading-relaxed relative [&_h1]:text-2xl [&_h2]:text-xl">
|
||||
<article className="space-y-5 [&_p]:leading-relaxed [&_p]:text-sm [&_li]:text-sm relative [&_h1]:text-2xl [&_h2]:text-xl">
|
||||
{children}
|
||||
</article>
|
||||
</div>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
# Privacy Policy
|
||||
|
||||
This website ("nonszy.space") fully respects the privacy of its visitors. This document explains how we handle visitor information. The short answer is: **we do not collect, track, or store your personal information**.
|
||||
This website ("nonszy.space") fully respects the privacy of its visitors. This page explains how we handle visitor information. The short answer is: **we do not collect, track, or store your personal information**.
|
||||
|
||||
However, future feature additions will update the privacy policy.
|
||||
132
src/components/wobbling-image.tsx
Normal file
132
src/components/wobbling-image.tsx
Normal file
@ -0,0 +1,132 @@
|
||||
'use client'
|
||||
|
||||
import clsx from "clsx";
|
||||
import Image from "next/image";
|
||||
import { useCallback, useEffect, useRef, useState } from "react"
|
||||
|
||||
interface WobblingImageInterface {
|
||||
img1: string
|
||||
img2?: string
|
||||
}
|
||||
|
||||
function WobblingImage ({
|
||||
img1, img2
|
||||
}: WobblingImageInterface) {
|
||||
const size = 400;
|
||||
const [isAnimating, setIsAnimating] = useState(false);
|
||||
|
||||
const audioPath = "/sound/poke.wav";
|
||||
const audioContextRef = useRef<AudioContext | null>(null);
|
||||
const [audioBuffer, setAudioBuffer] = useState<AudioBuffer | null>(null);
|
||||
const [audioLoaded, setAudioLoaded] = useState(false);
|
||||
|
||||
const [dummyAudio, setDummyAudio] = useState<HTMLAudioElement | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof Audio !== undefined) setDummyAudio(new Audio(audioPath))
|
||||
return () => {
|
||||
if (dummyAudio) dummyAudio.pause();
|
||||
}
|
||||
}, [])
|
||||
|
||||
const initializeAudioContext = useCallback((): AudioContext => {
|
||||
if (!audioContextRef.current) {
|
||||
audioContextRef.current = new (window.AudioContext || (window as any).webkitAudioContext)();
|
||||
}
|
||||
return audioContextRef.current;
|
||||
}, []);
|
||||
|
||||
const loadAudio = useCallback(async (url: string): Promise<AudioBuffer> => {
|
||||
const response = await fetch(url);
|
||||
const arrayBuffer = await response.arrayBuffer();
|
||||
const context = initializeAudioContext();
|
||||
return await context.decodeAudioData(arrayBuffer);
|
||||
}, [initializeAudioContext]);
|
||||
|
||||
const initializeAudio = useCallback(async (): Promise<void> => {
|
||||
if (audioBuffer) return;
|
||||
|
||||
setAudioLoaded(false);
|
||||
try {
|
||||
const buffer = await loadAudio(audioPath);
|
||||
setAudioBuffer(buffer);
|
||||
} catch (error) {
|
||||
console.error('Failed to load audio:', error);
|
||||
}
|
||||
finally {
|
||||
setAudioLoaded(true);
|
||||
}
|
||||
}, [audioBuffer, loadAudio]);
|
||||
|
||||
const playRandomPitch = useCallback(async (): Promise<void> => {
|
||||
await initializeAudio();
|
||||
|
||||
if (!audioBuffer || !audioContextRef.current) return;
|
||||
|
||||
const context = audioContextRef.current;
|
||||
const changeToLowPitch = Math.floor(Math.random() * 10);
|
||||
const randomPitch = Math.random() * 0.2 + (changeToLowPitch == 1 ? 0.25 : 0.85);
|
||||
|
||||
const source = context.createBufferSource();
|
||||
source.buffer = audioBuffer;
|
||||
source.playbackRate.value = randomPitch;
|
||||
source.connect(context.destination);
|
||||
source.start(0);
|
||||
|
||||
source.onended = () => {
|
||||
source.disconnect();
|
||||
};
|
||||
}, [audioBuffer, initializeAudio]);
|
||||
|
||||
|
||||
function handleClick() {
|
||||
setIsAnimating(false);
|
||||
requestAnimationFrame(() => setIsAnimating(true));
|
||||
playRandomPitch();
|
||||
if (dummyAudio && !audioLoaded) {
|
||||
dummyAudio.currentTime = 0;
|
||||
dummyAudio.play();
|
||||
}
|
||||
}
|
||||
|
||||
function handleAnimationEnd() {
|
||||
setIsAnimating(false);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="">
|
||||
<div
|
||||
className={clsx(
|
||||
"relative mx-auto cursor-grab h-[400px] select-none",
|
||||
isAnimating && "animate-[click-bounce_150ms_ease-out]"
|
||||
)}
|
||||
onClick={handleClick}
|
||||
onAnimationEnd={handleAnimationEnd}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); handleClick(); } }}
|
||||
onMouseDown={(e) => e.preventDefault()}
|
||||
onPointerDown={(e) => e.preventDefault()}
|
||||
>
|
||||
<Image
|
||||
className={clsx("absolute left-1/2 -translate-x-1/2 top-0 pointer-events-none", isAnimating ? "opacity-0" : "opacity-100")}
|
||||
alt="clip1"
|
||||
src={img1}
|
||||
width={size}
|
||||
height={size}
|
||||
draggable={false}
|
||||
/>
|
||||
<Image
|
||||
className={clsx("absolute left-1/2 -translate-x-1/2 top-0 pointer-events-none", isAnimating ? "opacity-100" : "opacity-0")}
|
||||
alt="clip2"
|
||||
src={img2 || img1}
|
||||
width={size}
|
||||
height={size}
|
||||
draggable={false}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default WobblingImage;
|
||||
@ -24,6 +24,7 @@ const config: Config = {
|
||||
},
|
||||
animation: {
|
||||
"silly-bouncing": 'silly-bounce 0.8s infinite',
|
||||
"click-bouncing": 'click-bounce 200ms',
|
||||
'window-popup': 'window-popup 1s',
|
||||
'window-popdown': 'window-popdown 1s'
|
||||
},
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user