From 3099e2cdf411d5939be18bc89b7fce431e45a0de Mon Sep 17 00:00:00 2001 From: Joseph Dale Banares Date: Wed, 29 Oct 2025 11:44:12 +0800 Subject: [PATCH 01/21] chore(deps): install ogl for minimal webgl --- bun.lock | 3 +++ package.json | 1 + 2 files changed, 4 insertions(+) diff --git a/bun.lock b/bun.lock index d216d58..f481297 100644 --- a/bun.lock +++ b/bun.lock @@ -30,6 +30,7 @@ "nanoid": "^5.1.6", "next": "15.5.6", "next-themes": "^0.4.6", + "ogl": "^1.0.11", "react": "^19.2.0", "react-dom": "^19.2.0", "react-intersection-observer": "^9.16.0", @@ -683,6 +684,8 @@ "node-fetch": ["node-fetch@3.3.2", "", { "dependencies": { "data-uri-to-buffer": "^4.0.0", "fetch-blob": "^3.1.4", "formdata-polyfill": "^4.0.10" } }, "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA=="], + "ogl": ["ogl@1.0.11", "", {}, "sha512-kUpC154AFfxi16pmZUK4jk3J+8zxwTWGPo03EoYA8QPbzikHoaC82n6pNTbd+oEaJonaE8aPWBlX7ad9zrqLsA=="], + "parse-entities": ["parse-entities@4.0.2", "", { "dependencies": { "@types/unist": "^2.0.0", "character-entities-legacy": "^3.0.0", "character-reference-invalid": "^2.0.0", "decode-named-character-reference": "^1.0.0", "is-alphanumerical": "^2.0.0", "is-decimal": "^2.0.0", "is-hexadecimal": "^2.0.0" } }, "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw=="], "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], diff --git a/package.json b/package.json index 4f0aa01..b3e50f6 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,7 @@ "nanoid": "^5.1.6", "next": "15.5.6", "next-themes": "^0.4.6", + "ogl": "^1.0.11", "react": "^19.2.0", "react-dom": "^19.2.0", "react-intersection-observer": "^9.16.0", From 878a485e8cd1198345aa7fc9af0618393e8f5368 Mon Sep 17 00:00:00 2001 From: Joseph Dale Banares Date: Wed, 29 Oct 2025 11:46:05 +0800 Subject: [PATCH 02/21] feat: landing light rays --- app/page.tsx | 16 +- components/LightRays.tsx | 450 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 465 insertions(+), 1 deletion(-) create mode 100644 components/LightRays.tsx diff --git a/app/page.tsx b/app/page.tsx index a6d0e4d..d36a988 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -2,12 +2,26 @@ import { MessageSquareTextIcon } from "lucide-react"; import Link from "next/link"; import { Footer } from "@/components/footer"; import { HighlightText } from "@/components/highlight-text"; +import LightRays from "@/components/LightRays"; import { Navbar } from "@/components/navbar"; import { Button } from "@/components/ui/button"; export default function Home() { return ( -
+
+
diff --git a/components/LightRays.tsx b/components/LightRays.tsx new file mode 100644 index 0000000..208530e --- /dev/null +++ b/components/LightRays.tsx @@ -0,0 +1,450 @@ +"use client"; + +import { Mesh, Program, Renderer, Triangle } from "ogl"; +import { useEffect, useRef, useState } from "react"; + +export type RaysOrigin = + | "top-center" + | "top-left" + | "top-right" + | "right" + | "left" + | "bottom-center" + | "bottom-right" + | "bottom-left"; + +interface LightRaysProps { + raysOrigin?: RaysOrigin; + raysColor?: string; + raysSpeed?: number; + lightSpread?: number; + rayLength?: number; + pulsating?: boolean; + fadeDistance?: number; + saturation?: number; + followMouse?: boolean; + mouseInfluence?: number; + noiseAmount?: number; + distortion?: number; + className?: string; +} + +const DEFAULT_COLOR = "#ffffff"; + +const hexToRgb = (hex: string): [number, number, number] => { + const m = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); + return m + ? [ + parseInt(m[1], 16) / 255, + parseInt(m[2], 16) / 255, + parseInt(m[3], 16) / 255, + ] + : [1, 1, 1]; +}; + +const getAnchorAndDir = ( + origin: RaysOrigin, + w: number, + h: number, +): { anchor: [number, number]; dir: [number, number] } => { + const outside = 0.2; + switch (origin) { + case "top-left": + return { anchor: [0, -outside * h], dir: [0, 1] }; + case "top-right": + return { anchor: [w, -outside * h], dir: [0, 1] }; + case "left": + return { anchor: [-outside * w, 0.5 * h], dir: [1, 0] }; + case "right": + return { anchor: [(1 + outside) * w, 0.5 * h], dir: [-1, 0] }; + case "bottom-left": + return { anchor: [0, (1 + outside) * h], dir: [0, -1] }; + case "bottom-center": + return { anchor: [0.5 * w, (1 + outside) * h], dir: [0, -1] }; + case "bottom-right": + return { anchor: [w, (1 + outside) * h], dir: [0, -1] }; + default: // "top-center" + return { anchor: [0.5 * w, -outside * h], dir: [0, 1] }; + } +}; + +const LightRays: React.FC = ({ + raysOrigin = "top-center", + raysColor = DEFAULT_COLOR, + raysSpeed = 1, + lightSpread = 1, + rayLength = 2, + pulsating = false, + fadeDistance = 1.0, + saturation = 1.0, + followMouse = true, + mouseInfluence = 0.1, + noiseAmount = 0.0, + distortion = 0.0, + className = "", +}) => { + const containerRef = useRef(null); + const uniformsRef = useRef(null); + const rendererRef = useRef(null); + const mouseRef = useRef({ x: 0.5, y: 0.5 }); + const smoothMouseRef = useRef({ x: 0.5, y: 0.5 }); + const animationIdRef = useRef(null); + const meshRef = useRef(null); + const cleanupFunctionRef = useRef<(() => void) | null>(null); + const [isVisible, setIsVisible] = useState(false); + const observerRef = useRef(null); + + useEffect(() => { + if (!containerRef.current) return; + + observerRef.current = new IntersectionObserver( + (entries) => { + const entry = entries[0]; + setIsVisible(entry.isIntersecting); + }, + { threshold: 0.1 }, + ); + + observerRef.current.observe(containerRef.current); + + return () => { + if (observerRef.current) { + observerRef.current.disconnect(); + observerRef.current = null; + } + }; + }, []); + + useEffect(() => { + if (!isVisible || !containerRef.current) return; + + if (cleanupFunctionRef.current) { + cleanupFunctionRef.current(); + cleanupFunctionRef.current = null; + } + + const initializeWebGL = async () => { + if (!containerRef.current) return; + + await new Promise((resolve) => setTimeout(resolve, 10)); + + if (!containerRef.current) return; + + const renderer = new Renderer({ + dpr: Math.min(window.devicePixelRatio, 2), + alpha: true, + }); + rendererRef.current = renderer; + + const gl = renderer.gl; + gl.canvas.style.width = "100%"; + gl.canvas.style.height = "100%"; + + while (containerRef.current.firstChild) { + containerRef.current.removeChild(containerRef.current.firstChild); + } + containerRef.current.appendChild(gl.canvas); + + const vert = ` +attribute vec2 position; +varying vec2 vUv; +void main() { + vUv = position * 0.5 + 0.5; + gl_Position = vec4(position, 0.0, 1.0); +}`; + + const frag = `precision highp float; + +uniform float iTime; +uniform vec2 iResolution; + +uniform vec2 rayPos; +uniform vec2 rayDir; +uniform vec3 raysColor; +uniform float raysSpeed; +uniform float lightSpread; +uniform float rayLength; +uniform float pulsating; +uniform float fadeDistance; +uniform float saturation; +uniform vec2 mousePos; +uniform float mouseInfluence; +uniform float noiseAmount; +uniform float distortion; + +varying vec2 vUv; + +float noise(vec2 st) { + return fract(sin(dot(st.xy, vec2(12.9898,78.233))) * 43758.5453123); +} + +float rayStrength(vec2 raySource, vec2 rayRefDirection, vec2 coord, + float seedA, float seedB, float speed) { + vec2 sourceToCoord = coord - raySource; + vec2 dirNorm = normalize(sourceToCoord); + float cosAngle = dot(dirNorm, rayRefDirection); + + float distortedAngle = cosAngle + distortion * sin(iTime * 2.0 + length(sourceToCoord) * 0.01) * 0.2; + + float spreadFactor = pow(max(distortedAngle, 0.0), 1.0 / max(lightSpread, 0.001)); + + float distance = length(sourceToCoord); + float maxDistance = iResolution.x * rayLength; + float lengthFalloff = clamp((maxDistance - distance) / maxDistance, 0.0, 1.0); + + float fadeFalloff = clamp((iResolution.x * fadeDistance - distance) / (iResolution.x * fadeDistance), 0.5, 1.0); + float pulse = pulsating > 0.5 ? (0.8 + 0.2 * sin(iTime * speed * 3.0)) : 1.0; + + float baseStrength = clamp( + (0.45 + 0.15 * sin(distortedAngle * seedA + iTime * speed)) + + (0.3 + 0.2 * cos(-distortedAngle * seedB + iTime * speed)), + 0.0, 1.0 + ); + + return baseStrength * lengthFalloff * fadeFalloff * spreadFactor * pulse; +} + +void mainImage(out vec4 fragColor, in vec2 fragCoord) { + vec2 coord = vec2(fragCoord.x, iResolution.y - fragCoord.y); + + vec2 finalRayDir = rayDir; + if (mouseInfluence > 0.0) { + vec2 mouseScreenPos = mousePos * iResolution.xy; + vec2 mouseDirection = normalize(mouseScreenPos - rayPos); + finalRayDir = normalize(mix(rayDir, mouseDirection, mouseInfluence)); + } + + vec4 rays1 = vec4(1.0) * + rayStrength(rayPos, finalRayDir, coord, 36.2214, 21.11349, + 1.5 * raysSpeed); + vec4 rays2 = vec4(1.0) * + rayStrength(rayPos, finalRayDir, coord, 22.3991, 18.0234, + 1.1 * raysSpeed); + + fragColor = rays1 * 0.5 + rays2 * 0.4; + + if (noiseAmount > 0.0) { + float n = noise(coord * 0.01 + iTime * 0.1); + fragColor.rgb *= (1.0 - noiseAmount + noiseAmount * n); + } + + float brightness = 1.0 - (coord.y / iResolution.y); + fragColor.x *= 0.1 + brightness * 0.8; + fragColor.y *= 0.3 + brightness * 0.6; + fragColor.z *= 0.5 + brightness * 0.5; + + if (saturation != 1.0) { + float gray = dot(fragColor.rgb, vec3(0.299, 0.587, 0.114)); + fragColor.rgb = mix(vec3(gray), fragColor.rgb, saturation); + } + + fragColor.rgb *= raysColor; +} + +void main() { + vec4 color; + mainImage(color, gl_FragCoord.xy); + gl_FragColor = color; +}`; + + const uniforms = { + iTime: { value: 0 }, + iResolution: { value: [1, 1] }, + + rayPos: { value: [0, 0] }, + rayDir: { value: [0, 1] }, + + raysColor: { value: hexToRgb(raysColor) }, + raysSpeed: { value: raysSpeed }, + lightSpread: { value: lightSpread }, + rayLength: { value: rayLength }, + pulsating: { value: pulsating ? 1.0 : 0.0 }, + fadeDistance: { value: fadeDistance }, + saturation: { value: saturation }, + mousePos: { value: [0.5, 0.5] }, + mouseInfluence: { value: mouseInfluence }, + noiseAmount: { value: noiseAmount }, + distortion: { value: distortion }, + }; + uniformsRef.current = uniforms; + + const geometry = new Triangle(gl); + const program = new Program(gl, { + vertex: vert, + fragment: frag, + uniforms, + }); + const mesh = new Mesh(gl, { geometry, program }); + meshRef.current = mesh; + + const updatePlacement = () => { + if (!containerRef.current || !renderer) return; + + renderer.dpr = Math.min(window.devicePixelRatio, 2); + + const { clientWidth: wCSS, clientHeight: hCSS } = containerRef.current; + renderer.setSize(wCSS, hCSS); + + const dpr = renderer.dpr; + const w = wCSS * dpr; + const h = hCSS * dpr; + + uniforms.iResolution.value = [w, h]; + + const { anchor, dir } = getAnchorAndDir(raysOrigin, w, h); + uniforms.rayPos.value = anchor; + uniforms.rayDir.value = dir; + }; + + const loop = (t: number) => { + if (!rendererRef.current || !uniformsRef.current || !meshRef.current) { + return; + } + + uniforms.iTime.value = t * 0.001; + + if (followMouse && mouseInfluence > 0.0) { + const smoothing = 0.92; + + smoothMouseRef.current.x = + smoothMouseRef.current.x * smoothing + + mouseRef.current.x * (1 - smoothing); + smoothMouseRef.current.y = + smoothMouseRef.current.y * smoothing + + mouseRef.current.y * (1 - smoothing); + + uniforms.mousePos.value = [ + smoothMouseRef.current.x, + smoothMouseRef.current.y, + ]; + } + + try { + renderer.render({ scene: mesh }); + animationIdRef.current = requestAnimationFrame(loop); + } catch (error) { + console.warn("WebGL rendering error:", error); + return; + } + }; + + window.addEventListener("resize", updatePlacement); + updatePlacement(); + animationIdRef.current = requestAnimationFrame(loop); + + cleanupFunctionRef.current = () => { + if (animationIdRef.current) { + cancelAnimationFrame(animationIdRef.current); + animationIdRef.current = null; + } + + window.removeEventListener("resize", updatePlacement); + + if (renderer) { + try { + const canvas = renderer.gl.canvas; + const loseContextExt = + renderer.gl.getExtension("WEBGL_lose_context"); + if (loseContextExt) { + loseContextExt.loseContext(); + } + + if (canvas && canvas.parentNode) { + canvas.parentNode.removeChild(canvas); + } + } catch (error) { + console.warn("Error during WebGL cleanup:", error); + } + } + + rendererRef.current = null; + uniformsRef.current = null; + meshRef.current = null; + }; + }; + + initializeWebGL(); + + return () => { + if (cleanupFunctionRef.current) { + cleanupFunctionRef.current(); + cleanupFunctionRef.current = null; + } + }; + }, [ + isVisible, + raysOrigin, + raysColor, + raysSpeed, + lightSpread, + rayLength, + pulsating, + fadeDistance, + saturation, + followMouse, + mouseInfluence, + noiseAmount, + distortion, + ]); + + useEffect(() => { + if (!uniformsRef.current || !containerRef.current || !rendererRef.current) + return; + + const u = uniformsRef.current; + const renderer = rendererRef.current; + + u.raysColor.value = hexToRgb(raysColor); + u.raysSpeed.value = raysSpeed; + u.lightSpread.value = lightSpread; + u.rayLength.value = rayLength; + u.pulsating.value = pulsating ? 1.0 : 0.0; + u.fadeDistance.value = fadeDistance; + u.saturation.value = saturation; + u.mouseInfluence.value = mouseInfluence; + u.noiseAmount.value = noiseAmount; + u.distortion.value = distortion; + + const { clientWidth: wCSS, clientHeight: hCSS } = containerRef.current; + const dpr = renderer.dpr; + const { anchor, dir } = getAnchorAndDir(raysOrigin, wCSS * dpr, hCSS * dpr); + u.rayPos.value = anchor; + u.rayDir.value = dir; + }, [ + raysColor, + raysSpeed, + lightSpread, + raysOrigin, + rayLength, + pulsating, + fadeDistance, + saturation, + mouseInfluence, + noiseAmount, + distortion, + ]); + + useEffect(() => { + const handleMouseMove = (e: MouseEvent) => { + if (!containerRef.current || !rendererRef.current) return; + const rect = containerRef.current.getBoundingClientRect(); + const x = (e.clientX - rect.left) / rect.width; + const y = (e.clientY - rect.top) / rect.height; + mouseRef.current = { x, y }; + }; + + if (followMouse) { + window.addEventListener("mousemove", handleMouseMove); + return () => window.removeEventListener("mousemove", handleMouseMove); + } + }, [followMouse]); + + return ( +
+ ); +}; + +export default LightRays; From d9dcd7573e601069f08e83d360c03323233e0c54 Mon Sep 17 00:00:00 2001 From: Joseph Dale Banares Date: Wed, 29 Oct 2025 12:11:44 +0800 Subject: [PATCH 03/21] chore(deps): install motion --- bun.lock | 9 +++++++++ package.json | 1 + 2 files changed, 10 insertions(+) diff --git a/bun.lock b/bun.lock index f481297..c8ffe8f 100644 --- a/bun.lock +++ b/bun.lock @@ -27,6 +27,7 @@ "drizzle-orm": "^0.44.7", "drizzle-seed": "^0.3.1", "lucide-react": "^0.548.0", + "motion": "^12.23.24", "nanoid": "^5.1.6", "next": "15.5.6", "next-themes": "^0.4.6", @@ -500,6 +501,8 @@ "formdata-polyfill": ["formdata-polyfill@4.0.10", "", { "dependencies": { "fetch-blob": "^3.1.2" } }, "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g=="], + "framer-motion": ["framer-motion@12.23.24", "", { "dependencies": { "motion-dom": "^12.23.23", "motion-utils": "^12.23.6", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-HMi5HRoRCTou+3fb3h9oTLyJGBxHfW+HnNE25tAXOvVx/IvwMHK0cx7IR4a2ZU6sh3IX1Z+4ts32PcYBOqka8w=="], + "get-nonce": ["get-nonce@1.0.1", "", {}, "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q=="], "get-tsconfig": ["get-tsconfig@4.10.0", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A=="], @@ -672,6 +675,12 @@ "micromark-util-types": ["micromark-util-types@2.0.2", "", {}, "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA=="], + "motion": ["motion@12.23.24", "", { "dependencies": { "framer-motion": "^12.23.24", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-Rc5E7oe2YZ72N//S3QXGzbnXgqNrTESv8KKxABR20q2FLch9gHLo0JLyYo2hZ238bZ9Gx6cWhj9VO0IgwbMjCw=="], + + "motion-dom": ["motion-dom@12.23.23", "", { "dependencies": { "motion-utils": "^12.23.6" } }, "sha512-n5yolOs0TQQBRUFImrRfs/+6X4p3Q4n1dUEqt/H58Vx7OW6RF+foWEgmTVDhIWJIMXOuNNL0apKH2S16en9eiA=="], + + "motion-utils": ["motion-utils@12.23.6", "", {}, "sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ=="], + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], "nanoid": ["nanoid@5.1.6", "", { "bin": { "nanoid": "bin/nanoid.js" } }, "sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg=="], diff --git a/package.json b/package.json index b3e50f6..21a7fab 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "drizzle-orm": "^0.44.7", "drizzle-seed": "^0.3.1", "lucide-react": "^0.548.0", + "motion": "^12.23.24", "nanoid": "^5.1.6", "next": "15.5.6", "next-themes": "^0.4.6", From b8d4e5dbdba6c37d25c777c8e70c44afd04bdadc Mon Sep 17 00:00:00 2001 From: Joseph Dale Banares Date: Wed, 29 Oct 2025 12:12:20 +0800 Subject: [PATCH 04/21] feat: effects on encrypted text --- app/page.tsx | 9 +- components/DecryptedText.tsx | 242 +++++++++++++++++++++++++++++++++++ 2 files changed, 250 insertions(+), 1 deletion(-) create mode 100644 components/DecryptedText.tsx diff --git a/app/page.tsx b/app/page.tsx index d36a988..087fb07 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,5 +1,6 @@ import { MessageSquareTextIcon } from "lucide-react"; import Link from "next/link"; +import DecryptedText from "@/components/DecryptedText"; import { Footer } from "@/components/footer"; import { HighlightText } from "@/components/highlight-text"; import LightRays from "@/components/LightRays"; @@ -29,7 +30,13 @@ export default function Home() { umedu

- open-source, anonymous, encrypted, private edu forums + open-source, anonymous, + + , private edu forums

diff --git a/components/ShinyText.tsx b/components/ShinyText.tsx new file mode 100644 index 0000000..26e0fb9 --- /dev/null +++ b/components/ShinyText.tsx @@ -0,0 +1,34 @@ +import type React from "react"; + +interface ShinyTextProps { + text: string; + disabled?: boolean; + duration?: number; + className?: string; +} + +const ShinyText: React.FC = ({ + text, + disabled = false, + duration = 5, + className = "", +}) => { + const animationDuration = `${duration}s`; + + return ( +
+ {text} +
+ ); +}; + +export default ShinyText; From 865cc9700d9af9e8d5db17dfe12e6950a649cd79 Mon Sep 17 00:00:00 2001 From: Joseph Dale Banares Date: Wed, 29 Oct 2025 12:39:54 +0800 Subject: [PATCH 06/21] chore: learn more animation --- app/page.tsx | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/app/page.tsx b/app/page.tsx index bbbd786..ad96826 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -72,8 +72,15 @@ export default function Home() { email. No personal information is stored, ensuring your privacy is protected.{" "} - - Learn more → + + Learn more{" "} + + {" "} + → +
From 591a6b10e2e05b3e6dc3b1ac5c8fd26ed35271d1 Mon Sep 17 00:00:00 2001 From: Joseph Dale Banares Date: Wed, 29 Oct 2025 13:37:22 +0800 Subject: [PATCH 07/21] chore: update shadcn registry --- components.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/components.json b/components.json index a64445d..f54adcf 100644 --- a/components.json +++ b/components.json @@ -10,6 +10,7 @@ "cssVariables": true, "prefix": "" }, + "iconLibrary": "lucide", "aliases": { "components": "@/components", "utils": "@/lib/utils", @@ -17,5 +18,7 @@ "lib": "@/lib", "hooks": "@/hooks" }, - "iconLibrary": "lucide" + "registries": { + "@magicui": "https://magicui.design/r/{name}.json" + } } From 1b829c4b386c9240766656641b3c6f119527cf10 Mon Sep 17 00:00:00 2001 From: Joseph Dale Banares Date: Wed, 29 Oct 2025 13:41:19 +0800 Subject: [PATCH 08/21] chore(deps): install dotted map --- bun.lock | 3 +++ package.json | 1 + 2 files changed, 4 insertions(+) diff --git a/bun.lock b/bun.lock index c8ffe8f..d568ec6 100644 --- a/bun.lock +++ b/bun.lock @@ -38,6 +38,7 @@ "react-markdown": "^10.1.0", "remark-gfm": "^4.0.1", "sonner": "^2.0.7", + "svg-dotted-map": "^2.0.1", "tailwind-merge": "^3.3.1", "tailwindcss-animate": "^1.0.7", "vaul": "^1.1.2", @@ -759,6 +760,8 @@ "styled-jsx": ["styled-jsx@5.1.6", "", { "dependencies": { "client-only": "0.0.1" }, "peerDependencies": { "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" } }, "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA=="], + "svg-dotted-map": ["svg-dotted-map@2.0.1", "", {}, "sha512-eeI2XzIKm23gmSVr7ASTMNVJvxAvBfyL30tN33Y/DcZCJXvC/Br/cxQp9Ts6jDK/e7fkE5TpZStEfduPqPXrIw=="], + "tailwind-merge": ["tailwind-merge@3.3.1", "", {}, "sha512-gBXpgUm/3rp1lMZZrM/w7D8GKqshif0zAymAhbCyIt8KMe+0v9DQ7cdYLR4FHH/cKpdTXb+A/tKKU3eolfsI+g=="], "tailwindcss": ["tailwindcss@4.1.16", "", {}, "sha512-pONL5awpaQX4LN5eiv7moSiSPd/DLDzKVRJz8Q9PgzmAdd1R4307GQS2ZpfiN7ZmekdQrfhZZiSE5jkLR4WNaA=="], diff --git a/package.json b/package.json index 21a7fab..d87f118 100644 --- a/package.json +++ b/package.json @@ -51,6 +51,7 @@ "react-markdown": "^10.1.0", "remark-gfm": "^4.0.1", "sonner": "^2.0.7", + "svg-dotted-map": "^2.0.1", "tailwind-merge": "^3.3.1", "tailwindcss-animate": "^1.0.7", "vaul": "^1.1.2", From 923c0671ca192d63534207957ada138f658f9fe6 Mon Sep 17 00:00:00 2001 From: Joseph Dale Banares Date: Wed, 29 Oct 2025 13:43:51 +0800 Subject: [PATCH 09/21] chore: update footer --- components/footer.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/footer.tsx b/components/footer.tsx index 71550b3..f5115ea 100644 --- a/components/footer.tsx +++ b/components/footer.tsx @@ -8,8 +8,8 @@ export function Footer() { umedu {" "} by{" "} - - @josh.xfi + + @omsimos
From 95c8992fa188fac7e906c69965223504e8773a16 Mon Sep 17 00:00:00 2001 From: Joseph Dale Banares Date: Wed, 29 Oct 2025 13:44:15 +0800 Subject: [PATCH 10/21] feat: dotted map ui --- app/page.tsx | 7 ++- components/flat-map.tsx | 88 ++++++++++++++++++++++++++++++ components/ui/dotted-map.tsx | 103 +++++++++++++++++++++++++++++++++++ 3 files changed, 196 insertions(+), 2 deletions(-) create mode 100644 components/flat-map.tsx create mode 100644 components/ui/dotted-map.tsx diff --git a/app/page.tsx b/app/page.tsx index ad96826..b61a44c 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,6 +1,7 @@ import { MessageSquareTextIcon } from "lucide-react"; import Link from "next/link"; import DecryptedText from "@/components/DecryptedText"; +import { FlatMap } from "@/components/flat-map"; import { Footer } from "@/components/footer"; import { HighlightText } from "@/components/highlight-text"; import LightRays from "@/components/LightRays"; @@ -22,7 +23,7 @@ export default function Home() { mouseInfluence={0.1} noiseAmount={0.1} distortion={0} - className="custom-rays absolute inset-0 -z-10 opacity-50" + className="custom-rays absolute inset-0 -z-10 opacity-40" />
@@ -85,7 +86,9 @@ export default function Home() {
- +
+ +