From 6fda511c3898dfae91990f1c72940bc2811eb613 Mon Sep 17 00:00:00 2001 From: Solo Shun <88045720+soloshun@users.noreply.github.com> Date: Tue, 27 Jan 2026 06:55:49 +0000 Subject: [PATCH] feat(app): add coming soon and not found page --- apps/app/package.json | 6 +- apps/app/src/app/globals.css | 49 ++- apps/app/src/app/layout.tsx | 37 +- apps/app/src/app/not-found.tsx | 315 ++++++++++++++++ apps/app/src/app/page.tsx | 658 ++++++++++++++++++++++++++++++--- pnpm-lock.yaml | 12 + 6 files changed, 996 insertions(+), 81 deletions(-) create mode 100644 apps/app/src/app/not-found.tsx diff --git a/apps/app/package.json b/apps/app/package.json index 4027230..ddd53f9 100644 --- a/apps/app/package.json +++ b/apps/app/package.json @@ -9,9 +9,13 @@ "lint": "eslint" }, "dependencies": { + "clsx": "^2.1.1", + "framer-motion": "^12.29.0", + "lucide-react": "^0.563.0", "next": "16.1.4", "react": "19.2.3", - "react-dom": "19.2.3" + "react-dom": "19.2.3", + "tailwind-merge": "^3.4.0" }, "devDependencies": { "@tailwindcss/postcss": "^4", diff --git a/apps/app/src/app/globals.css b/apps/app/src/app/globals.css index a2dc41e..a6c56c1 100644 --- a/apps/app/src/app/globals.css +++ b/apps/app/src/app/globals.css @@ -1,26 +1,47 @@ @import "tailwindcss"; :root { - --background: #ffffff; - --foreground: #171717; + --background: 0 0% 3%; + --foreground: 0 0% 98%; + --card: 0 0% 6%; + --card-foreground: 0 0% 98%; + --primary: 142 71% 45%; + --primary-foreground: 0 0% 2%; + --secondary: 0 0% 10%; + --muted: 0 0% 15%; + --muted-foreground: 0 0% 60%; + --border: 0 0% 15%; + --ring: 142 71% 45%; } @theme inline { - --color-background: var(--background); - --color-foreground: var(--foreground); - --font-sans: var(--font-geist-sans); - --font-mono: var(--font-geist-mono); + --color-background: hsl(var(--background)); + --color-foreground: hsl(var(--foreground)); + --color-primary: hsl(var(--primary)); + --color-primary-foreground: hsl(var(--primary-foreground)); + --font-sans: var(--font-syne), system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; + --font-mono: var(--font-jetbrains), ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace; } -@media (prefers-color-scheme: dark) { - :root { - --background: #0a0a0a; - --foreground: #ededed; - } +* { + box-sizing: border-box; + padding: 0; + margin: 0; } +html, body { - background: var(--background); - color: var(--foreground); - font-family: Arial, Helvetica, sans-serif; + background: hsl(var(--background)); + color: hsl(var(--foreground)); + min-height: 100vh; + overflow-x: hidden; +} + +/* Font classes */ +.font-sans { + font-family: var(--font-syne), system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; +} + +.font-mono { + font-family: var(--font-jetbrains), ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace; } diff --git a/apps/app/src/app/layout.tsx b/apps/app/src/app/layout.tsx index f7fa87e..de7057e 100644 --- a/apps/app/src/app/layout.tsx +++ b/apps/app/src/app/layout.tsx @@ -1,20 +1,37 @@ import type { Metadata } from "next"; -import { Geist, Geist_Mono } from "next/font/google"; +import { JetBrains_Mono, Syne } from "next/font/google"; import "./globals.css"; -const geistSans = Geist({ - variable: "--font-geist-sans", +const syne = Syne({ + variable: "--font-syne", subsets: ["latin"], + weight: ["400", "500", "600", "700", "800"], + display: "swap", }); -const geistMono = Geist_Mono({ - variable: "--font-geist-mono", +const jetbrainsMono = JetBrains_Mono({ + variable: "--font-jetbrains", subsets: ["latin"], + display: "swap", }); export const metadata: Metadata = { - title: "Create Next App", - description: "Generated by create next app", + title: "OpenDSA App - Coming Soon", + description: "The open-source algorithm visualization platform. Interactive visualizations for sorting, searching, graphs, trees, and more.", + keywords: ["algorithms", "data structures", "visualization", "learning", "education", "open source"], + authors: [{ name: "Solomon Eshun", url: "https://github.com/soloshun" }], + openGraph: { + title: "OpenDSA - Coming Soon", + description: "The open-source algorithm visualization platform is under development.", + url: "https://app.opendsa.dev", + siteName: "OpenDSA", + type: "website", + }, + twitter: { + card: "summary_large_image", + title: "OpenDSA - Coming Soon", + description: "The open-source algorithm visualization platform is under development.", + }, }; export default function RootLayout({ @@ -23,10 +40,8 @@ export default function RootLayout({ children: React.ReactNode; }>) { return ( - -
+ + {children} diff --git a/apps/app/src/app/not-found.tsx b/apps/app/src/app/not-found.tsx new file mode 100644 index 0000000..71172e4 --- /dev/null +++ b/apps/app/src/app/not-found.tsx @@ -0,0 +1,315 @@ +"use client"; + +import { useState, useEffect } from "react"; +import Link from "next/link"; + +// Glitch text effect +function GlitchText({ text }: { text: string }) { + const [glitchedText, setGlitchedText] = useState(text); + const [mounted, setMounted] = useState(false); + + useEffect(() => { + // eslint-disable-next-line react-hooks/set-state-in-effect + setMounted(true); + }, []); + + useEffect(() => { + if (!mounted) return; + + const glitchChars = "!@#$%^&*()_+-=[]{}|;:,.<>?"; + let interval: NodeJS.Timeout; + + const startGlitch = () => { + let iterations = 0; + interval = setInterval(() => { + setGlitchedText( + text + .split("") + .map((char, index) => { + if (index < iterations) return text[index]; + return glitchChars[Math.floor(Math.random() * glitchChars.length)]; + }) + .join("") + ); + iterations += 1 / 3; + if (iterations >= text.length) { + clearInterval(interval); + setGlitchedText(text); + } + }, 30); + }; + + startGlitch(); + const loopInterval = setInterval(startGlitch, 5000); + + return () => { + clearInterval(interval); + clearInterval(loopInterval); + }; + }, [text, mounted]); + + if (!mounted) return {text}; + + return {glitchedText}; +} + +// ASCII art 404 +const ascii404 = ` + ██╗ ██╗ ██████╗ ██╗ ██╗ + ██║ ██║██╔═████╗██║ ██║ + ███████║██║██╔██║███████║ + ╚════██║████╔╝██║╚════██║ + ██║╚██████╔╝ ██║ + ╚═╝ ╚═════╝ ╚═╝ +`; + +export default function NotFound() { + const [mounted, setMounted] = useState(false); + const [currentPath, setCurrentPath] = useState("/unknown"); + + useEffect(() => { + // eslint-disable-next-line react-hooks/set-state-in-effect + setMounted(true); + setCurrentPath(window.location.pathname); + }, []); + + if (!mounted) { + return ( + + ); + } + + return ( +
+ {ascii404}
+
+
+ {/* Terminal window */}
+ + Looks like you've ventured into uncharted territory. The page you're looking for doesn't exist or has been moved. +
+- Looking for a starting point or more instructions? Head over to{" "} - - Templates - {" "} - or the{" "} - - Learning - {" "} - center. +
+ v0.1.0-alpha
+ We're building something amazing. The open-source algorithm visualization platform is under active development. +
++ Made with 💚 by{" "} + + @soloshun + + {" "}• MIT License +