Skip to content

Commit b273616

Browse files
author
meowabyte
committed
Codebase Overhaul:
+ Version bump 1.1.1 -> 1.2.0 + Structure of the codebase modified for better maintainability + Fixed incorrectly labeled buttons + Added some additional styles for buttons + Added Error handler for WebGL not being enabled (3D Renderer) and fixed firefox specific CSS bugs + Added better SSR + Added cool matrix background + More code optimizations - Disabled typescript checks on build for more performance and because IDE's checker does it's job already - Silenced unfixable source map tailwind error (tailwindlabs/tailwindcss#13694) ...and more!
1 parent 796b368 commit b273616

File tree

21 files changed

+390
-126
lines changed

21 files changed

+390
-126
lines changed

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
{
22
"name": "meowabyte.github.io",
3-
"version": "1.1.1",
3+
"version": "1.2.0",
44
"private": true,
55
"license": "MIT",
66
"type": "module",
77
"scripts": {
88
"dev": "vite",
9-
"build": "tsc -b && vite build",
9+
"build": "vite build",
1010
"preview": "vite preview"
1111
},
1212
"dependencies": {
13+
"tailwindcss": "^4.1.7",
1314
"lucide-preact": "^0.511.0",
1415
"preact": "^10.26.5",
1516
"preact-iso": "^2.9.1",
@@ -22,7 +23,6 @@
2223
"lightningcss": "^1.30.1",
2324
"sharp": "^0.34.2",
2425
"svgo": "^3.3.2",
25-
"tailwindcss": "^4.1.7",
2626
"typescript": "~5.8.3",
2727
"vite": "^6.3.5",
2828
"vite-bundle-analyzer": "^0.21.0",

src/app/__prerender.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ if (typeof window !== "undefined") throw new Error("PLEASE DO NOT IMPORT THIS OU
22

33
import { prerender as ssr } from "preact-iso"
44
import { App } from "./app"
5-
import { getMetadata } from "../helpers/metadata";
5+
import { getMetadata } from "../helpers/prerender/metadata";
66

77

88
const REQUIRED_PRERENDERS = ["/", "/404"] as const

src/app/app.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ export function App() {
88
return <LocationProvider>
99
<ErrorBoundary>
1010
<Router>
11-
<Route path="/" component={lazy(() => import("./page"))} />
11+
<Route path="/" component={lazy(() => import("./home"))} />
1212
<Route path="/404" component={lazy(() => import("./not-found"))} />
1313
<Route default component={() => <Navigate to="/404" />} />
1414
</Router>

src/components/menu/body/aboutme.tsx renamed to src/app/home-sections/aboutme.tsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11

2+
import ModalBody from "../../components/modal/modalbody";
23

3-
import { calculateAge } from "../../../helpers/utils";
4-
import { ModalBody } from "../modal";
5-
6-
const age = calculateAge(new Date(2005, 4, 27))
4+
const age = (() => {
5+
const diff = Date.now() - new Date(2005, 4, 27).getTime();
6+
const ageDate = new Date(diff);
7+
return Math.abs(ageDate.getUTCFullYear() - 1970);
8+
})()
79

810
export default function AboutMe() {
911
return <ModalBody className="grid max-lg:w-4/5 max-md:grid-rows-[0.5fr_2fr] md:grid-cols-[1fr_2fr] gap-5">

src/components/menu/body/projects.tsx renamed to src/app/home-sections/projects.tsx

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import { HardDrive, Link2, StarIcon } from "lucide-preact";
2-
import { ModalBody } from "../modal";
2+
import ModalBody from "../../components/modal/modalbody";
33
import { SOCIALS } from "./social"
44
import { useCallback } from "preact/hooks";
5-
import languageNames from "../../../helpers/languagenames";
65

76

87
type RepoData = { name: string, description: string, stars: number, language: string }
@@ -34,6 +33,14 @@ const FEATURED_REPOS: RepoData[] = [
3433
}
3534
] as const
3635

36+
const languageColors = new Map([
37+
["CSS", "#663399"],
38+
["HTML", "#e34c26"],
39+
["JavaScript", "#f1e05a"],
40+
["Rust", "#dea584"],
41+
["TypeScript", "#3178c6"],
42+
])
43+
3744
function Project({ repo: { name, description, stars, language } }: { repo: RepoData }) {
3845
const visitRepo = useCallback(() => window.open(`https://github.com/${SOCIALS.github}/${name}`), [])
3946

@@ -44,7 +51,7 @@ function Project({ repo: { name, description, stars, language } }: { repo: RepoD
4451
</div>
4552
<span className="min-h-12">{description}</span>
4653
<div className="flex flex-row justify-between *:flex *:flex-row *:gap-1 *:items-center">
47-
<span><div style={{ "--language-color": languageNames.get(language) ?? "#fff" } as any} className="bg-[var(--language-color)] w-[1em] h-[1em] rounded-sm" /> {language}</span>
54+
<span><div style={{ "--language-color": languageColors.get(language) ?? "#fff" } as any} className="bg-[var(--language-color)] w-[1em] h-[1em] rounded-sm" /> {language}</span>
4855
<span><StarIcon size="1.3em" className="inline" /> ±{stars}</span>
4956
</div>
5057
</div>

src/components/menu/body/social.tsx renamed to src/app/home-sections/social.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { useCallback, useMemo } from "preact/hooks";
2-
import { ModalBody } from "../modal";
2+
import ModalBody from "../../components/modal/modalbody";
33

44
const LINKS = new Map([
55
["youtube", "youtube.com"],

src/app/home.tsx

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
import "../styles/main.css"
2+
3+
import { useCallback, useEffect, useRef, useState } from "preact/hooks"
4+
import { cn, sleep } from "../helpers/utils"
5+
import Menu, { MenuProvider } from "../components/menu";
6+
import MenuButton from "../components/menu/menubutton";
7+
import SillyRenderer from "../components/silly";
8+
import { lazy } from "preact-iso";
9+
10+
const MatrixBG = lazy(() => import("../components/bg/matrix"))
11+
12+
const TITLE_TEXT = "Welcome to meowpa.ws!" as const
13+
const TYPEWRITER_DELAY = 30 as const
14+
15+
const GLITCH_CHARACTERS = ["@", "#", "/", "\\", "&", "%"] as const
16+
17+
export default function Home() {
18+
const [isBlinking, setBlinking] = useState(true)
19+
const [animationState, setAnimationState] = useState(0)
20+
const titleRef = useRef<HTMLSpanElement>(null)
21+
22+
const startTyping = useCallback(async (clear?: boolean) => {
23+
if (!titleRef.current) return;
24+
25+
let lastBlinkingState = isBlinking;
26+
setBlinking(true)
27+
28+
if (clear) titleRef.current.textContent = ""
29+
for(const c of TITLE_TEXT) {
30+
titleRef.current.textContent += c
31+
await sleep(TYPEWRITER_DELAY)
32+
}
33+
34+
setBlinking(lastBlinkingState)
35+
}, [titleRef])
36+
37+
// Preloading actions
38+
useEffect(() => {
39+
if (animationState === 2) MatrixBG.preload() // BG Preload
40+
}, [animationState])
41+
42+
// Intro Animation
43+
useEffect(() => {
44+
const animate = async () => {
45+
await sleep(800)
46+
.then(() => startTyping(true))
47+
48+
for (let i = 0; i < 3; i++)
49+
await sleep(400)
50+
.then(() => setAnimationState(s => s+1))
51+
}
52+
53+
if (document.readyState !== "loading") animate()
54+
else document.addEventListener("DOMContentLoaded", animate, { once: true })
55+
56+
return () => {
57+
document.removeEventListener("DOMContentLoaded", animate)
58+
}
59+
}, [])
60+
61+
// Title glitch effect
62+
useEffect(() => {
63+
if (animationState < 2 || !titleRef.current) return;
64+
65+
const validChars = titleRef.current!.textContent!
66+
.split("")
67+
.reduce<number[]>((arr, c, i) => {
68+
if (c !== " ") arr.push(i)
69+
return arr
70+
}, [])
71+
72+
const i = setInterval(async () => {
73+
const charIndex = validChars[Math.floor(Math.random() * validChars.length)]
74+
const replChar = GLITCH_CHARACTERS[Math.floor(Math.random() * GLITCH_CHARACTERS.length)]
75+
76+
const oldTitle = titleRef.current!.textContent!
77+
const newTitle = oldTitle.slice(0, charIndex) + replChar + oldTitle.slice(charIndex + 1)
78+
79+
titleRef.current!.textContent! = newTitle
80+
await sleep(100)
81+
.then(() => titleRef.current!.textContent! = oldTitle)
82+
}, 1500)
83+
84+
return () => {
85+
clearInterval(i)
86+
}
87+
}, [animationState, titleRef])
88+
89+
const animationSlide = useCallback((id: number) => cn(
90+
"transition duration-200",
91+
animationState < id && "opacity-0 -translate-y-5"
92+
), [animationState])
93+
94+
return (
95+
<div class="roboto-mono">
96+
<MenuProvider>
97+
<div class="select-none fixed left-1/2 top-1/2 -translate-1/2 text-center h-1/2 w-5/6 flex flex-col items-center">
98+
<span ref={titleRef} className={cn('text-3xl after:content-["▊"] after:ml-1', isBlinking && "after:animate-[blink_0.5s_alternate_infinite]")}></span>
99+
100+
<Menu className={animationSlide(1)}>
101+
<MenuButton lazy element={lazy(() => import("./home-sections/aboutme"))}>About Me</MenuButton>
102+
<MenuButton lazy element={lazy(() => import("./home-sections/projects"))}>My Projects</MenuButton>
103+
<MenuButton lazy element={lazy(() => import("./home-sections/social"))}>My Social Media</MenuButton>
104+
</Menu>
105+
106+
<SillyRenderer className={animationSlide(2)} height={250} width={250} />
107+
</div>
108+
</MenuProvider>
109+
{animationState >= 3 && <MatrixBG className="brightness-90" />}
110+
</div>
111+
)
112+
}

src/app/not-found.tsx

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
11
import NikoLook from "../img/nikolook.webp"
22

33
export default function NotFound() {
4-
return (
5-
<div className="flex flex-col items-center h-screen justify-center">
6-
<img src={NikoLook} alt="NikoLook" width={100} height={100} />
7-
</div>
8-
)
4+
return <img src={NikoLook} alt="NikoLook" width={100} height={100} class="fixed inset-1/2 -translate-1/2" />
95
}

src/app/page.tsx

Lines changed: 0 additions & 17 deletions
This file was deleted.

src/components/bg/matrix.tsx

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
import { useEffect, useRef, useState } from "preact/hooks"
2+
import { cn, sleep } from "../../helpers/utils";
3+
4+
const UPDATE_DELAY = 500 as const
5+
const UPDATE_FADE_OPACITY = 0.2 as const
6+
const UPDATE_CHARACTERS = ["@", "#", "/", "\\", "&", "%"] as const
7+
8+
9+
const FONT_NAME = "Roboto Mono" as const
10+
const FONT_SIZE = 16 as const
11+
const FONT = `400 ${FONT_SIZE}px "${FONT_NAME}"` as const
12+
const FONT_PX = (() => {
13+
const c = document.createElement("canvas")
14+
.getContext("2d")!
15+
16+
c.font = FONT
17+
return c.measureText("@")
18+
})()
19+
20+
const FONT_JUMP_SIZE = FONT_PX.actualBoundingBoxAscent + 10
21+
const UPDATE_FADE_SIZE = FONT_JUMP_SIZE * (Math.ceil(1 / UPDATE_FADE_OPACITY) * 4)
22+
23+
24+
type BMAddToMatrixOptions = {
25+
/** Position of the element. Random by default */
26+
x?: number,
27+
/** fillStyle of the element. White by default */
28+
style?: string,
29+
/** Delay between ticks */
30+
delay?: number
31+
}
32+
33+
class BackgroundManager {
34+
target: HTMLCanvasElement;
35+
ctx: CanvasRenderingContext2D;
36+
updateInterval?: number;
37+
38+
constructor(target: HTMLCanvasElement) {
39+
this.target = target;
40+
this.ctx = this.target.getContext("2d", { willReadFrequently: true })!
41+
42+
this.setupCanvas()
43+
this.setupEvents()
44+
}
45+
46+
setupCanvas() {
47+
const canvasData = [this.target.width, this.target.height].includes(0)
48+
? null
49+
: this.ctx.getImageData(0, 0, this.target.width, this.target.height)
50+
51+
this.target.width = window.innerWidth
52+
this.target.height = window.innerHeight
53+
54+
if (canvasData) this.ctx.putImageData(canvasData, 0, 0)
55+
}
56+
57+
setupEvents() {
58+
window.addEventListener("resize", this.setupCanvas.bind(this))
59+
60+
document.fonts.ready
61+
.then(() => {
62+
this.updateInterval = setInterval(this.addToMatrix.bind(this), UPDATE_DELAY) as unknown as number;
63+
this.addToMatrix()
64+
})
65+
}
66+
67+
destroy() {
68+
window.removeEventListener("resize", this.setupCanvas.bind(this))
69+
if (this.updateInterval) clearInterval(this.updateInterval)
70+
}
71+
72+
73+
74+
async addToMatrix(o?: BMAddToMatrixOptions) {
75+
const x = (() => {
76+
const x = o?.x ?? Math.floor(Math.random() * this.target.width)
77+
return x - x % FONT_PX.width
78+
})()
79+
80+
for (let y = 0; y < this.target.height + UPDATE_FADE_SIZE; y += FONT_JUMP_SIZE) {
81+
if (x > this.target.width) break; // Resized - disable if outside of area
82+
83+
// Char
84+
this.ctx.fillStyle = o?.style ?? "white"
85+
this.ctx.font = FONT
86+
this.ctx.fillText(
87+
UPDATE_CHARACTERS[Math.floor(Math.random() * UPDATE_CHARACTERS.length)],
88+
x, y
89+
)
90+
91+
// Fading
92+
this.ctx.fillStyle = `rgba(0,0,0,${UPDATE_FADE_OPACITY})`
93+
this.ctx.fillRect(
94+
x, y,
95+
FONT_PX.width, -UPDATE_FADE_SIZE
96+
)
97+
98+
await sleep(o?.delay ?? 100)
99+
}
100+
}
101+
}
102+
103+
export default function MatrixBG({ className }: { className?: string }) {
104+
const ref = useRef<HTMLCanvasElement>(null)
105+
const [bg, setBg] = useState<BackgroundManager | null>(null)
106+
107+
// BG add to matrix effect
108+
useEffect(() => {
109+
if (!bg) return;
110+
111+
const addToMatrix = ({ x }: MouseEvent) => {
112+
bg.addToMatrix({
113+
x,
114+
style: "red",
115+
delay: 30
116+
})
117+
}
118+
119+
window.addEventListener("click", addToMatrix)
120+
return () => { window.removeEventListener("click", addToMatrix) }
121+
}, [bg])
122+
123+
// Init BG
124+
useEffect(() => {
125+
if (!ref.current) return;
126+
127+
const bg = new BackgroundManager(ref.current)
128+
setBg(bg)
129+
130+
return () => { bg.destroy() }
131+
}, [])
132+
133+
return <canvas
134+
class={cn(className, "fixed left-0 top-0 -z-[9999] pointer-events-none")}
135+
ref={ref}
136+
/>
137+
}

0 commit comments

Comments
 (0)