From a997230ed7cac53506cf78032420cdca60e8db7a Mon Sep 17 00:00:00 2001 From: Solo Shun <88045720+soloshun@users.noreply.github.com> Date: Sat, 24 Jan 2026 19:41:26 +0000 Subject: [PATCH 1/7] feat: OpenDSA landing page(init dev, random links) --- apps/web/next.config.ts | 11 +- apps/web/package.json | 8 +- apps/web/src/app/globals.css | 213 +++++++++- apps/web/src/app/layout.tsx | 51 ++- apps/web/src/app/page.tsx | 84 ++-- .../components/animations/ai-assistant.tsx | 103 +++++ .../animations/architecture-flow.tsx | 244 ++++++++++++ .../components/animations/array-search.tsx | 95 +++++ .../src/components/animations/binary-tree.tsx | 118 ++++++ .../components/animations/code-preview.tsx | 58 +++ .../animations/graph-visualization.tsx | 126 ++++++ .../components/animations/keyboard-demo.tsx | 71 ++++ .../components/animations/sorting-bars.tsx | 77 ++++ .../src/components/sections/architecture.tsx | 330 ++++++++++++++++ apps/web/src/components/sections/faq.tsx | 174 +++++++++ apps/web/src/components/sections/features.tsx | 268 +++++++++++++ apps/web/src/components/sections/footer.tsx | 181 +++++++++ apps/web/src/components/sections/header.tsx | 138 +++++++ apps/web/src/components/sections/hero.tsx | 147 +++++++ .../src/components/sections/open-source.tsx | 176 +++++++++ apps/web/src/components/sections/roadmap.tsx | 240 ++++++++++++ .../src/components/sections/tech-stack.tsx | 148 +++++++ apps/web/src/components/ui/bento-grid.tsx | 76 ++++ apps/web/src/components/ui/button.tsx | 97 +++++ .../components/ui/particles-background.tsx | 112 ++++++ apps/web/src/lib/utils.ts | 6 + pnpm-lock.yaml | 367 ++++++++++++++++++ 27 files changed, 3634 insertions(+), 85 deletions(-) create mode 100644 apps/web/src/components/animations/ai-assistant.tsx create mode 100644 apps/web/src/components/animations/architecture-flow.tsx create mode 100644 apps/web/src/components/animations/array-search.tsx create mode 100644 apps/web/src/components/animations/binary-tree.tsx create mode 100644 apps/web/src/components/animations/code-preview.tsx create mode 100644 apps/web/src/components/animations/graph-visualization.tsx create mode 100644 apps/web/src/components/animations/keyboard-demo.tsx create mode 100644 apps/web/src/components/animations/sorting-bars.tsx create mode 100644 apps/web/src/components/sections/architecture.tsx create mode 100644 apps/web/src/components/sections/faq.tsx create mode 100644 apps/web/src/components/sections/features.tsx create mode 100644 apps/web/src/components/sections/footer.tsx create mode 100644 apps/web/src/components/sections/header.tsx create mode 100644 apps/web/src/components/sections/hero.tsx create mode 100644 apps/web/src/components/sections/open-source.tsx create mode 100644 apps/web/src/components/sections/roadmap.tsx create mode 100644 apps/web/src/components/sections/tech-stack.tsx create mode 100644 apps/web/src/components/ui/bento-grid.tsx create mode 100644 apps/web/src/components/ui/button.tsx create mode 100644 apps/web/src/components/ui/particles-background.tsx create mode 100644 apps/web/src/lib/utils.ts diff --git a/apps/web/next.config.ts b/apps/web/next.config.ts index e9ffa30..2965787 100644 --- a/apps/web/next.config.ts +++ b/apps/web/next.config.ts @@ -1,7 +1,16 @@ import type { NextConfig } from "next"; const nextConfig: NextConfig = { - /* config options here */ + images: { + remotePatterns: [ + { + protocol: "https", + hostname: "avatars.githubusercontent.com", + port: "", + pathname: "/**", + }, + ], + }, }; export default nextConfig; diff --git a/apps/web/package.json b/apps/web/package.json index 99df217..1db7ac8 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -9,9 +9,15 @@ "lint": "eslint" }, "dependencies": { + "@tsparticles/react": "^3.0.0", + "@tsparticles/slim": "^3.9.1", + "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/web/src/app/globals.css b/apps/web/src/app/globals.css index a2dc41e..1d7b03e 100644 --- a/apps/web/src/app/globals.css +++ b/apps/web/src/app/globals.css @@ -1,26 +1,211 @@ @import "tailwindcss"; :root { - --background: #ffffff; - --foreground: #171717; + --background: 0 0% 4%; + --foreground: 0 0% 98%; + --card: 0 0% 7%; + --card-foreground: 0 0% 98%; + --primary: 142 71% 45%; + --primary-foreground: 0 0% 4%; + --secondary: 0 0% 11%; + --secondary-foreground: 0 0% 98%; + --muted: 0 0% 15%; + --muted-foreground: 0 0% 64%; + --accent: 142 71% 45%; + --accent-foreground: 0 0% 4%; + --border: 0 0% 18%; + --ring: 142 71% 45%; + --radius: 0.75rem; } @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-card: hsl(var(--card)); + --color-card-foreground: hsl(var(--card-foreground)); + --color-primary: hsl(var(--primary)); + --color-primary-foreground: hsl(var(--primary-foreground)); + --color-secondary: hsl(var(--secondary)); + --color-secondary-foreground: hsl(var(--secondary-foreground)); + --color-muted: hsl(var(--muted)); + --color-muted-foreground: hsl(var(--muted-foreground)); + --color-accent: hsl(var(--accent)); + --color-accent-foreground: hsl(var(--accent-foreground)); + --color-border: hsl(var(--border)); + --color-ring: hsl(var(--ring)); + --font-sans: var(--font-syne), system-ui, sans-serif; + --font-mono: var(--font-jetbrains-mono), ui-monospace, monospace; } -@media (prefers-color-scheme: dark) { - :root { - --background: #0a0a0a; - --foreground: #ededed; - } +* { + border-color: hsl(var(--border)); +} + +html { + scroll-behavior: smooth; } body { - background: var(--background); - color: var(--foreground); - font-family: Arial, Helvetica, sans-serif; + background-color: hsl(var(--background)); + color: hsl(var(--foreground)); + font-family: var(--font-syne), system-ui, sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +/* Custom scrollbar */ +::-webkit-scrollbar { + width: 6px; + height: 6px; +} + +::-webkit-scrollbar-track { + background: hsl(var(--background)); } + +::-webkit-scrollbar-thumb { + background: hsl(var(--border)); + border-radius: 3px; +} + +::-webkit-scrollbar-thumb:hover { + background: hsl(var(--muted-foreground)); +} + +/* Gradient text utility */ +.gradient-text { + background: linear-gradient(135deg, hsl(142 71% 45%) 0%, hsl(142 69% 58%) 50%, hsl(142 77% 73%) 100%); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; +} + +/* Glowing effect */ +.glow { + box-shadow: 0 0 20px hsla(142, 71%, 45%, 0.4), + 0 0 40px hsla(142, 71%, 45%, 0.2), + 0 0 60px hsla(142, 71%, 45%, 0.1); +} + +.glow-sm { + box-shadow: 0 0 10px hsla(142, 71%, 45%, 0.3), + 0 0 20px hsla(142, 71%, 45%, 0.15); +} + +.glow-text { + text-shadow: 0 0 30px hsla(142, 71%, 45%, 0.6), + 0 0 60px hsla(142, 71%, 45%, 0.4); +} + +/* Shimmer animation */ +@keyframes shimmer { + 0% { + background-position: -200% 0; + } + + 100% { + background-position: 200% 0; + } +} + +.shimmer { + background: linear-gradient(90deg, + transparent 0%, + hsla(142, 71%, 45%, 0.08) 50%, + transparent 100%); + background-size: 200% 100%; + animation: shimmer 3s infinite; +} + +/* Code block styling */ +.code-block { + background: linear-gradient(135deg, hsl(0 0% 5%) 0%, hsl(0 0% 9%) 100%); + border: 1px solid hsl(var(--border)); + border-radius: var(--radius); +} + +/* Grid pattern background */ +.grid-pattern { + background-image: + linear-gradient(hsla(142, 71%, 45%, 0.04) 1px, transparent 1px), + linear-gradient(90deg, hsla(142, 71%, 45%, 0.04) 1px, transparent 1px); + background-size: 60px 60px; +} + +/* Noise texture overlay */ +.noise { + position: relative; +} + +.noise::before { + content: ""; + position: absolute; + inset: 0; + background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 512 512' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noiseFilter'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noiseFilter)'/%3E%3C/svg%3E"); + opacity: 0.03; + pointer-events: none; + z-index: 1; +} + +/* Highlight box - like the yellow box in OpenReel */ +.highlight-box { + background: hsl(var(--primary)); + color: hsl(var(--primary-foreground)); + padding: 0.125rem 0.5rem; + display: inline-block; +} + +/* Glass effect */ +.glass { + background: hsla(var(--background), 0.7); + backdrop-filter: blur(20px); + -webkit-backdrop-filter: blur(20px); +} + +/* Animated gradient border */ +@keyframes border-glow { + + 0%, + 100% { + border-color: hsla(142, 71%, 45%, 0.3); + } + + 50% { + border-color: hsla(142, 71%, 45%, 0.6); + } +} + +.border-glow { + animation: border-glow 3s ease-in-out infinite; +} + +/* Underline decoration like OpenReel */ +.underline-primary { + position: relative; + display: inline-block; +} + +.underline-primary::after { + content: ""; + position: absolute; + bottom: -4px; + left: 0; + right: 0; + height: 3px; + background: hsl(var(--primary)); +} + +/* Monospace label styling */ +.mono-label { + font-family: var(--font-mono); + font-size: 0.75rem; + letter-spacing: 0.1em; + text-transform: uppercase; + color: hsl(var(--primary)); +} + +/* Selection color */ +::selection { + background: hsla(142, 71%, 45%, 0.3); + color: hsl(var(--foreground)); +} \ No newline at end of file diff --git a/apps/web/src/app/layout.tsx b/apps/web/src/app/layout.tsx index f7fa87e..14c3663 100644 --- a/apps/web/src/app/layout.tsx +++ b/apps/web/src/app/layout.tsx @@ -1,20 +1,51 @@ import type { Metadata } from "next"; -import { Geist, Geist_Mono } from "next/font/google"; +import { Syne, JetBrains_Mono } from "next/font/google"; import "./globals.css"; -const geistSans = Geist({ - variable: "--font-geist-sans", +const syne = Syne({ + variable: "--font-syne", subsets: ["latin"], + display: "swap", + weight: ["400", "500", "600", "700", "800"], }); -const geistMono = Geist_Mono({ - variable: "--font-geist-mono", +const jetbrainsMono = JetBrains_Mono({ + variable: "--font-jetbrains-mono", subsets: ["latin"], + display: "swap", + weight: ["400", "500", "600", "700"], }); export const metadata: Metadata = { - title: "Create Next App", - description: "Generated by create next app", + title: "OpenDSA - Visualize Data Structures & Algorithms", + description: + "An open-source, interactive platform for visualizing data structures and algorithms. Learn through beautiful animations and step-by-step explanations.", + keywords: [ + "algorithms", + "data structures", + "visualization", + "learning", + "open source", + "education", + "sorting", + "searching", + "graphs", + "trees", + ], + authors: [{ name: "Solomon Eshun" }], + openGraph: { + title: "OpenDSA - Visualize Data Structures & Algorithms", + description: + "An open-source, interactive platform for visualizing data structures and algorithms.", + type: "website", + locale: "en_US", + }, + twitter: { + card: "summary_large_image", + title: "OpenDSA - Visualize Data Structures & Algorithms", + description: + "An open-source, interactive platform for visualizing data structures and algorithms.", + }, }; export default function RootLayout({ @@ -23,10 +54,8 @@ export default function RootLayout({ children: React.ReactNode; }>) { return ( - - + + {children} diff --git a/apps/web/src/app/page.tsx b/apps/web/src/app/page.tsx index 295f8fd..a676623 100644 --- a/apps/web/src/app/page.tsx +++ b/apps/web/src/app/page.tsx @@ -1,65 +1,33 @@ -import Image from "next/image"; +import { Header } from "@/components/sections/header"; +import { Hero } from "@/components/sections/hero"; +import { Features } from "@/components/sections/features"; +import { Architecture } from "@/components/sections/architecture"; +import { TechStack } from "@/components/sections/tech-stack"; +import { Roadmap } from "@/components/sections/roadmap"; +import { FAQ } from "@/components/sections/faq"; +import { OpenSource } from "@/components/sections/open-source"; +import { Footer } from "@/components/sections/footer"; +import { ParticlesBackground } from "@/components/ui/particles-background"; export default function Home() { return ( -
-
- Next.js logo -
-

- To get started, edit the page.tsx file. -

-

- Looking for a starting point or more instructions? Head over to{" "} - - Templates - {" "} - or the{" "} - - Learning - {" "} - center. -

-
-
- - Vercel logomark - Deploy Now - - - Documentation - -
+
+ {/* Global particles network - covers entire page */} +
+ +
+ +
+
+ + + + + + +
+
); } diff --git a/apps/web/src/components/animations/ai-assistant.tsx b/apps/web/src/components/animations/ai-assistant.tsx new file mode 100644 index 0000000..98d36e5 --- /dev/null +++ b/apps/web/src/components/animations/ai-assistant.tsx @@ -0,0 +1,103 @@ +"use client"; + +import { motion } from "framer-motion"; +import { useEffect, useState } from "react"; +import { Sparkles } from "lucide-react"; + +export function AIAssistant() { + const [currentStep, setCurrentStep] = useState(0); + const [displayText, setDisplayText] = useState(""); + + const steps = [ + { prompt: "Explain bubble sort", response: "Bubble sort compares adjacent elements..." }, + { prompt: "Optimize this code", response: "Use quick sort for O(n log n)..." }, + { prompt: "Show me BFS", response: "Starting BFS from node A..." }, + ]; + + useEffect(() => { + const currentResponse = steps[currentStep].response; + let charIndex = 0; + setDisplayText(""); + + const typeInterval = setInterval(() => { + if (charIndex < currentResponse.length) { + setDisplayText(currentResponse.slice(0, charIndex + 1)); + charIndex++; + } else { + clearInterval(typeInterval); + setTimeout(() => { + setCurrentStep((prev) => (prev + 1) % steps.length); + }, 2000); + } + }, 50); + + return () => clearInterval(typeInterval); + }, [currentStep]); + + return ( +
+ {/* AI Chat */} +
+ {/* User prompt */} +
+
+ U +
+
+ {steps[currentStep].prompt} +
+
+ + {/* AI response */} +
+
+ +
+
+
+ {displayText} + +
+
+
+
+ + {/* Divider */} +
+
+ EDITOR +
+
+ + {/* Mini Code Editor */} +
+ {/* Editor tabs */} +
+ {["JS", "PY", "C++"].map((lang, i) => ( + + {lang} + + ))} +
+ {/* Mini code */} +
+
function sort(arr) {"{"}
+
// AI optimized
+
...
+
{"}"}
+
+
+
+ ); +} diff --git a/apps/web/src/components/animations/architecture-flow.tsx b/apps/web/src/components/animations/architecture-flow.tsx new file mode 100644 index 0000000..3b68787 --- /dev/null +++ b/apps/web/src/components/animations/architecture-flow.tsx @@ -0,0 +1,244 @@ +"use client"; + +import { motion } from "framer-motion"; +import { useEffect, useState } from "react"; + +// Plugin nodes +const plugins = [ + { id: "bubble", label: "BubbleSortPlugin", x: 50, y: 0 }, + { id: "quick", label: "QuickSortPlugin", x: 50, y: 20 }, + { id: "merge", label: "MergeSortPlugin", x: 50, y: 40 }, + { id: "binary", label: "BinarySearchPlugin", x: 50, y: 60 }, + { id: "bfs", label: "BFSPlugin", x: 50, y: 80 }, +]; + +// Registry nodes +const registryNodes = [ + { id: "register", label: "register", x: 60, y: 50 }, + { id: "get", label: "get", x: 40, y: 70 }, + { id: "getByCategory", label: "getByCategory", x: 60, y: 70 }, + { id: "getAll", label: "getAll", x: 80, y: 70 }, +]; + +// App nodes +const appNodes = [ + { id: "router", label: "Dynamic Router", x: 20, y: 50 }, + { id: "renderer", label: "Visualizer Renderer", x: 20, y: 85 }, +]; + +export function ArchitectureFlow() { + const [activePlugin, setActivePlugin] = useState(0); + const [flowStep, setFlowStep] = useState(0); + + useEffect(() => { + const interval = setInterval(() => { + setFlowStep((prev) => { + if (prev >= 4) { + setActivePlugin((p) => (p + 1) % plugins.length); + return 0; + } + return prev + 1; + }); + }, 800); + + return () => clearInterval(interval); + }, []); + + return ( +
+ + {/* Background sections */} + {/* Plugins Section */} + + + Visualizer Plugins + + + {/* Registry Section */} + + + Visualizer Registry + + + {/* App Section */} + + + Application + + + {/* Connection lines */} + {/* Plugin to Register */} + = 1 ? "hsl(142 71% 45%)" : "hsl(0 0% 25%)"} + strokeWidth={flowStep >= 1 ? "0.8" : "0.4"} + fill="none" + markerEnd="url(#arrowhead)" + /> + + {/* Register to Get */} + + + {/* Router to Get */} + = 2 ? "hsl(142 71% 45%)" : "hsl(0 0% 25%)"} + strokeWidth={flowStep >= 2 ? "0.8" : "0.4"} + fill="none" + markerEnd="url(#arrowhead)" + /> + + {/* Get to Renderer */} + = 3 ? "hsl(142 71% 45%)" : "hsl(0 0% 25%)"} + strokeWidth={flowStep >= 3 ? "0.8" : "0.4"} + fill="none" + markerEnd="url(#arrowhead)" + /> + + {/* Arrow marker */} + + + + + + + {/* Flowing dot animation */} + {flowStep === 1 && ( + + )} + {flowStep === 2 && ( + + )} + {flowStep === 3 && ( + + )} + + {/* Plugin nodes */} + {plugins.map((plugin, index) => ( + + = 0 ? "hsl(142 71% 45% / 0.2)" : "hsl(0 0% 8%)"} + stroke={activePlugin === index && flowStep >= 0 ? "hsl(142 71% 45%)" : "hsl(0 0% 25%)"} + strokeWidth="0.4" + animate={{ + fill: activePlugin === index && flowStep >= 0 ? "hsl(142 71% 45% / 0.2)" : "hsl(0 0% 8%)", + stroke: activePlugin === index && flowStep >= 0 ? "hsl(142 71% 45%)" : "hsl(0 0% 25%)", + }} + /> + = 0 ? "hsl(142 71% 45%)" : "hsl(0 0% 60%)"} + fontSize="2.5" + fontFamily="monospace" + > + {plugin.label} + + + ))} + + {/* Registry nodes */} + + = 1 ? "hsl(142 71% 45% / 0.2)" : "hsl(0 0% 8%)"} + stroke={flowStep >= 1 ? "hsl(142 71% 45%)" : "hsl(0 0% 25%)"} + strokeWidth="0.4" + /> + = 1 ? "hsl(142 71% 45%)" : "hsl(0 0% 60%)"} fontSize="2.5" fontFamily="monospace"> + register + + + + + = 2 ? "hsl(142 71% 45% / 0.2)" : "hsl(0 0% 8%)"} + stroke={flowStep >= 2 ? "hsl(142 71% 45%)" : "hsl(0 0% 25%)"} + strokeWidth="0.4" + /> + = 2 ? "hsl(142 71% 45%)" : "hsl(0 0% 60%)"} fontSize="2" fontFamily="monospace"> + get + + + + + + + getByCategory + + + + {/* App nodes */} + + = 2 ? "hsl(142 71% 45% / 0.2)" : "hsl(0 0% 8%)"} + stroke={flowStep >= 2 ? "hsl(142 71% 45%)" : "hsl(0 0% 25%)"} + strokeWidth="0.4" + /> + = 2 ? "hsl(142 71% 45%)" : "hsl(0 0% 60%)"} fontSize="2.2" fontFamily="monospace"> + Dynamic Router + + + + + = 3 ? "hsl(142 71% 45% / 0.2)" : "hsl(0 0% 8%)"} + stroke={flowStep >= 3 ? "hsl(142 71% 45%)" : "hsl(0 0% 25%)"} + strokeWidth="0.4" + /> + = 3 ? "hsl(142 71% 45%)" : "hsl(0 0% 60%)"} fontSize="2" fontFamily="monospace"> + Visualizer Renderer + + + + + {/* Current step indicator */} +
+ {[0, 1, 2, 3, 4].map((step) => ( +
+ ))} +
+
+ ); +} diff --git a/apps/web/src/components/animations/array-search.tsx b/apps/web/src/components/animations/array-search.tsx new file mode 100644 index 0000000..6624f80 --- /dev/null +++ b/apps/web/src/components/animations/array-search.tsx @@ -0,0 +1,95 @@ +"use client"; + +import { motion } from "framer-motion"; +import { useEffect, useState } from "react"; + +export function ArraySearch() { + const array = [2, 5, 8, 11, 12, 16, 23, 28, 38, 56, 60, 67, 72, 81, 91, 99, 123, 212]; + const target = 67; + const [currentIndex, setCurrentIndex] = useState(-1); + const [left, setLeft] = useState(0); + const [right, setRight] = useState(array.length - 1); + const [found, setFound] = useState(false); + + useEffect(() => { + const animateBinarySearch = async () => { + setCurrentIndex(-1); + setLeft(0); + setRight(array.length - 1); + setFound(false); + + let l = 0; + let r = array.length - 1; + + while (l <= r) { + const mid = Math.floor((l + r) / 2); + setCurrentIndex(mid); + setLeft(l); + setRight(r); + await new Promise((resolve) => setTimeout(resolve, 700)); + + if (array[mid] === target) { + setFound(true); + break; + } else if (array[mid] < target) { + l = mid + 1; + } else { + r = mid - 1; + } + } + + await new Promise((resolve) => setTimeout(resolve, 2500)); + }; + + const interval = setInterval(animateBinarySearch, 8000); + animateBinarySearch(); + + return () => clearInterval(interval); + }, []); + + return ( +
+
+ {array.map((num, index) => { + const isInRange = index >= left && index <= right; + const isCurrent = index === currentIndex; + const isFound = found && isCurrent; + + return ( + + {num} + + ); + })} +
+
+ Target: {target} + {found && ( + + Found! + + )} +
+
+ ); +} diff --git a/apps/web/src/components/animations/binary-tree.tsx b/apps/web/src/components/animations/binary-tree.tsx new file mode 100644 index 0000000..bd14586 --- /dev/null +++ b/apps/web/src/components/animations/binary-tree.tsx @@ -0,0 +1,118 @@ +"use client"; + +import { motion } from "framer-motion"; +import { useEffect, useState } from "react"; + +interface TreeNode { + value: number; + x: number; + y: number; + left?: TreeNode; + right?: TreeNode; +} + +export function BinaryTree() { + const [visitedNodes, setVisitedNodes] = useState([]); + + const tree: TreeNode = { + value: 50, + x: 100, + y: 20, + left: { + value: 30, + x: 50, + y: 60, + left: { value: 20, x: 25, y: 100 }, + right: { value: 40, x: 75, y: 100 }, + }, + right: { + value: 70, + x: 150, + y: 60, + left: { value: 60, x: 125, y: 100 }, + right: { value: 80, x: 175, y: 100 }, + }, + }; + + const getAllNodes = (node: TreeNode | undefined): TreeNode[] => { + if (!node) return []; + return [node, ...getAllNodes(node.left), ...getAllNodes(node.right)]; + }; + + const nodes = getAllNodes(tree); + + useEffect(() => { + const animateTraversal = async () => { + setVisitedNodes([]); + const order = [50, 30, 20, 40, 70, 60, 80]; + + for (const value of order) { + await new Promise((resolve) => setTimeout(resolve, 500)); + setVisitedNodes((prev) => [...prev, value]); + } + + await new Promise((resolve) => setTimeout(resolve, 2000)); + }; + + const interval = setInterval(animateTraversal, 8000); + animateTraversal(); + + return () => clearInterval(interval); + }, []); + + const edges = [ + { x1: 100, y1: 30, x2: 50, y2: 55 }, + { x1: 100, y1: 30, x2: 150, y2: 55 }, + { x1: 50, y1: 70, x2: 25, y2: 95 }, + { x1: 50, y1: 70, x2: 75, y2: 95 }, + { x1: 150, y1: 70, x2: 125, y2: 95 }, + { x1: 150, y1: 70, x2: 175, y2: 95 }, + ]; + + return ( + + {/* Edges */} + {edges.map((edge, index) => ( + + ))} + + {/* Nodes */} + {nodes.map((node) => ( + + + + {node.value} + + + ))} + + ); +} diff --git a/apps/web/src/components/animations/code-preview.tsx b/apps/web/src/components/animations/code-preview.tsx new file mode 100644 index 0000000..e755e12 --- /dev/null +++ b/apps/web/src/components/animations/code-preview.tsx @@ -0,0 +1,58 @@ +"use client"; + +import { motion } from "framer-motion"; +import { useEffect, useState } from "react"; + +export function CodePreview() { + const [highlightedLine, setHighlightedLine] = useState(0); + + const codeLines = [ + { content: "function bubbleSort(arr) {", indent: 0 }, + { content: "for (let i = 0; i < arr.length; i++) {", indent: 1 }, + { content: "for (let j = 0; j < arr.length - i - 1; j++) {", indent: 2 }, + { content: "if (arr[j] > arr[j + 1]) {", indent: 3 }, + { content: "[arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];", indent: 4 }, + { content: "}", indent: 3 }, + { content: "}", indent: 2 }, + { content: "}", indent: 1 }, + { content: "return arr;", indent: 1 }, + { content: "}", indent: 0 }, + ]; + + useEffect(() => { + const interval = setInterval(() => { + setHighlightedLine((prev) => (prev + 1) % codeLines.length); + }, 800); + + return () => clearInterval(interval); + }, [codeLines.length]); + + return ( +
+ {codeLines.map((line, index) => ( + + + {index + 1} + + + {line.content} + + + ))} +
+ ); +} diff --git a/apps/web/src/components/animations/graph-visualization.tsx b/apps/web/src/components/animations/graph-visualization.tsx new file mode 100644 index 0000000..500b3c7 --- /dev/null +++ b/apps/web/src/components/animations/graph-visualization.tsx @@ -0,0 +1,126 @@ +"use client"; + +import { motion } from "framer-motion"; +import { useEffect, useState } from "react"; + +interface Node { + id: string; + x: number; + y: number; +} + +interface Edge { + from: string; + to: string; +} + +export function GraphVisualization() { + const [visitedNodes, setVisitedNodes] = useState([]); + const [visitedEdges, setVisitedEdges] = useState([]); + + const nodes: Node[] = [ + { id: "A", x: 50, y: 30 }, + { id: "B", x: 100, y: 20 }, + { id: "C", x: 150, y: 40 }, + { id: "D", x: 30, y: 80 }, + { id: "E", x: 90, y: 90 }, + { id: "F", x: 160, y: 100 }, + ]; + + const edges: Edge[] = [ + { from: "A", to: "B" }, + { from: "A", to: "D" }, + { from: "B", to: "C" }, + { from: "B", to: "E" }, + { from: "D", to: "E" }, + { from: "C", to: "F" }, + { from: "E", to: "F" }, + ]; + + useEffect(() => { + const animateBFS = async () => { + setVisitedNodes([]); + setVisitedEdges([]); + + const order = ["A", "B", "D", "C", "E", "F"]; + const edgeOrder = ["A-B", "A-D", "B-C", "B-E", "D-E", "C-F"]; + + for (let i = 0; i < order.length; i++) { + await new Promise((resolve) => setTimeout(resolve, 400)); + setVisitedNodes((prev) => [...prev, order[i]]); + if (edgeOrder[i]) { + setVisitedEdges((prev) => [...prev, edgeOrder[i]]); + } + } + + await new Promise((resolve) => setTimeout(resolve, 2500)); + }; + + const interval = setInterval(animateBFS, 7000); + animateBFS(); + + return () => clearInterval(interval); + }, []); + + const getNodePos = (id: string) => nodes.find((n) => n.id === id)!; + + return ( + + {/* Edges */} + {edges.map((edge) => { + const from = getNodePos(edge.from); + const to = getNodePos(edge.to); + const edgeId = `${edge.from}-${edge.to}`; + const isVisited = visitedEdges.includes(edgeId); + + return ( + + ); + })} + + {/* Nodes */} + {nodes.map((node) => { + const isVisited = visitedNodes.includes(node.id); + + return ( + + + + {node.id} + + + ); + })} + + ); +} diff --git a/apps/web/src/components/animations/keyboard-demo.tsx b/apps/web/src/components/animations/keyboard-demo.tsx new file mode 100644 index 0000000..783779b --- /dev/null +++ b/apps/web/src/components/animations/keyboard-demo.tsx @@ -0,0 +1,71 @@ +"use client"; + +import { motion } from "framer-motion"; +import { useEffect, useState } from "react"; + +const keys = [ + { key: "Space", label: "Play/Pause", width: "flex-1" }, + { key: "←", label: "Prev Step", width: "w-10" }, + { key: "→", label: "Next Step", width: "w-10" }, + { key: "R", label: "Reset", width: "w-10" }, +]; + +export function KeyboardDemo() { + const [activeKey, setActiveKey] = useState(null); + const [currentIndex, setCurrentIndex] = useState(0); + + useEffect(() => { + const interval = setInterval(() => { + const key = keys[currentIndex].key; + setActiveKey(key); + + setTimeout(() => { + setActiveKey(null); + }, 300); + + setCurrentIndex((prev) => (prev + 1) % keys.length); + }, 1500); + + return () => clearInterval(interval); + }, [currentIndex]); + + return ( +
+ {/* Keyboard row */} +
+ {keys.map(({ key, width }) => ( + + {key} + + ))} +
+ + {/* Current action indicator */} +
+ + {keys[currentIndex].key} + + {keys[currentIndex].label} + +
+
+ ); +} diff --git a/apps/web/src/components/animations/sorting-bars.tsx b/apps/web/src/components/animations/sorting-bars.tsx new file mode 100644 index 0000000..fd955a6 --- /dev/null +++ b/apps/web/src/components/animations/sorting-bars.tsx @@ -0,0 +1,77 @@ +"use client"; + +import { motion } from "framer-motion"; +import { useEffect, useState, useRef } from "react"; + +export function SortingBars() { + const [bars, setBars] = useState([45, 80, 30, 95, 55, 70, 25, 85, 40, 60]); + const [activeIndices, setActiveIndices] = useState([]); + const [sortedIndices, setSortedIndices] = useState([]); + const isSorting = useRef(false); + + useEffect(() => { + const animateBubbleSort = async () => { + if (isSorting.current) return; + isSorting.current = true; + + const arr = [45, 80, 30, 95, 55, 70, 25, 85, 40, 60]; + setBars([...arr]); + setSortedIndices([]); + setActiveIndices([]); + + await new Promise((resolve) => setTimeout(resolve, 500)); + + const n = arr.length; + + for (let i = 0; i < n - 1; i++) { + for (let j = 0; j < n - i - 1; j++) { + // Highlight comparing elements + setActiveIndices([j, j + 1]); + await new Promise((resolve) => setTimeout(resolve, 200)); + + // Swap if needed + if (arr[j] > arr[j + 1]) { + [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]]; + setBars([...arr]); + await new Promise((resolve) => setTimeout(resolve, 150)); + } + } + // Mark the last element of this pass as sorted + setSortedIndices((prev) => [...prev, n - 1 - i]); + } + + // Mark first element as sorted too + setSortedIndices((prev) => [...prev, 0]); + setActiveIndices([]); + + // Wait before resetting + await new Promise((resolve) => setTimeout(resolve, 2500)); + isSorting.current = false; + }; + + animateBubbleSort(); + const interval = setInterval(animateBubbleSort, 8000); + + return () => clearInterval(interval); + }, []); + + return ( +
+ {bars.map((height, index) => ( + + ))} +
+ ); +} diff --git a/apps/web/src/components/sections/architecture.tsx b/apps/web/src/components/sections/architecture.tsx new file mode 100644 index 0000000..4bc4dbf --- /dev/null +++ b/apps/web/src/components/sections/architecture.tsx @@ -0,0 +1,330 @@ +"use client"; + +import { motion } from "framer-motion"; +import { Settings2, ArrowRight } from "lucide-react"; +import { useState, useEffect } from "react"; +import { ArchitectureFlow } from "@/components/animations/architecture-flow"; + +const codeTabs = [ + { + name: "plugin.ts", + code: `interface VisualizerPlugin { + meta: { + id: "bubble-sort", + name: "Bubble Sort", + category: "sorting", + }, + generateSteps(arr: number[]): Step[], + component: React.FC, +}`, + }, + { + name: "types.ts", + code: `interface AnimationStep { + type: "compare" | "swap" | "done"; + indices: number[]; + description: string; +} + +type VisualizerCategory = + | "sorting" + | "searching" + | "graph";`, + }, + { + name: "registry.ts", + code: `const registry = new Map(); + +export function register(plugin) { + registry.set(plugin.meta.id, plugin); +} + +export function get(id: string) { + return registry.get(id); +}`, + }, +]; + +const flowSteps = [ + { id: "plugin", label: "BubbleSortPlugin", color: "primary" }, + { id: "register", label: "registry.register()", color: "yellow" }, + { id: "registry", label: "Visualizer Registry", color: "blue" }, + { id: "router", label: "/visualize/bubble-sort", color: "purple" }, + { id: "render", label: "Renderer", color: "primary" }, +]; + +export function Architecture() { + const [activeTab, setActiveTab] = useState(0); + const [activeFlowStep, setActiveFlowStep] = useState(0); + const [showDiagram, setShowDiagram] = useState(false); + + // Animate through flow steps + useEffect(() => { + const interval = setInterval(() => { + setActiveFlowStep((prev) => (prev + 1) % flowSteps.length); + }, 1500); + return () => clearInterval(interval); + }, []); + + return ( +
+ {/* Background */} +
+
+ + {/* Noise overlay */} +
+
+
+ +
+ {/* Top section: Text + Code Editor */} +
+ {/* Left side - Content */} + + {/* Label */} +
+ + / System Architecture +
+ + {/* Heading */} +

+ PLUGIN-BASED +
+ VISUALIZER ENGINE +

+ + {/* Description */} +

+ Every algorithm is a self-contained plugin with its own visualization logic. + This makes OpenDSA extensible, maintainable, and community-friendly. +

+ + {/* Features list */} +
    + {[ + "Step-based animation system", + "Modular visualizer registry", + "Shared UI components", + "URL state sync", + ].map((item, index) => ( + + + {item} + + ))} +
+
+ + {/* Right side - Code Editor with Tabs */} + +
+ {/* Browser header */} +
+
+
+
+
+
+ {/* Clickable tabs */} +
+ {codeTabs.map((tab, index) => ( + + ))} +
+
+ + {/* Code content */} + +
+                  
+                    {codeTabs[activeTab].code.split("\n").map((line, i) => (
+                      
+ {line.includes("interface") || line.includes("type") || line.includes("const") || line.includes("export") ? ( + {line.split(" ")[0]} + ) : null} + {line.includes("interface") || line.includes("type") ? ( + {line.split(" ").slice(1).join(" ")} + ) : line.includes("const") || line.includes("export") || line.includes("function") ? ( + {line.split(" ").slice(1).join(" ")} + ) : line.includes(":") && !line.includes("//") ? ( + <> + {line.split(":")[0]} + :{line.split(":").slice(1).join(":")} + + ) : line.includes('"') ? ( + {line} + ) : ( + {line} + )} +
+ ))} +
+
+
+
+ + {/* Decorative gradient */} +
+ +
+ + {/* Middle section: Toggle between Flow types */} + + {/* Flow title with toggle */} +
+
+ / Plugin Registration Flow +
+
+
+ + +
+
+ + {/* Flow visualization */} +
+ {/* Noise overlay */} +
+
+
+ + {showDiagram ? ( + /* Architecture Diagram Flow */ +
+ +
+ ) : ( + /* Linear Flow */ + <> +
+ {flowSteps.map((step, index) => ( +
+ + {activeFlowStep === index && ( + + )} + + {step.label} + + + + {/* Arrow */} + {index < flowSteps.length - 1 && ( + + + + )} +
+ ))} +
+ + {/* Step description */} + + + {activeFlowStep === 0 && "Plugin defines meta, component, and step generator"} + {activeFlowStep === 1 && "Plugin is registered with the central registry"} + {activeFlowStep === 2 && "Registry stores all available visualizers"} + {activeFlowStep === 3 && "URL routes to specific visualizer by ID"} + {activeFlowStep === 4 && "Renderer loads and displays the visualization"} + + + + )} +
+ +
+
+ ); +} diff --git a/apps/web/src/components/sections/faq.tsx b/apps/web/src/components/sections/faq.tsx new file mode 100644 index 0000000..3584e45 --- /dev/null +++ b/apps/web/src/components/sections/faq.tsx @@ -0,0 +1,174 @@ +"use client"; + +import { motion, AnimatePresence } from "framer-motion"; +import { ChevronRight, Terminal, MessageSquare } from "lucide-react"; +import Link from "next/link"; +import { useState } from "react"; + +interface FAQItem { + id: string; + question: string; + answer: string; +} + +const faqItems: FAQItem[] = [ + { + id: "0x01", + question: "IS OPENDSA REALLY FREE?", + answer: "Yes! OpenDSA is 100% free and open source under the MIT license. No subscriptions, no premium tiers, no hidden costs. Ever.", + }, + { + id: "0x02", + question: "WHAT ALGORITHMS ARE SUPPORTED?", + answer: "We support sorting algorithms (bubble, quick, merge, heap, insertion, selection), searching (binary, linear), graph algorithms (BFS, DFS, Dijkstra's), and tree operations (BST, AVL). More are being added regularly.", + }, + { + id: "0x03", + question: "CAN I CONTRIBUTE NEW ALGORITHMS?", + answer: "Absolutely! We welcome contributions. Check our CONTRIBUTING.md guide on GitHub to get started. Each visualizer is a modular plugin, making it easy to add new ones.", + }, + { + id: "0x04", + question: "DOES IT WORK OFFLINE?", + answer: "Yes, once loaded, the visualizations work entirely in your browser. No server calls needed for running animations or stepping through algorithms.", + }, + { + id: "0x05", + question: "CAN I USE IT FOR TEACHING?", + answer: "Yes! OpenDSA is perfect for educators. Share specific visualization states via URL, embed visualizers in your course materials, and let students explore at their own pace.", + }, +]; + +export function FAQ() { + const [openId, setOpenId] = useState(null); + + return ( +
+ {/* Background */} +
+
+ + {/* Noise overlay */} +
+
+
+ +
+
+ {/* Left side - Header */} + + {/* Label */} +
+ + / HELP_TERMINAL +
+ + {/* Heading - stacked words */} +

+ FREQ. +
+ ASKED +
+ QUEST. +

+ + {/* Description */} +

+ Common questions about licensing, architecture, and technical capabilities. +

+ + {/* Contact card */} + +
+
+
+
+ +

+ Still have questions? +

+ + JOIN DISCUSSIONS + + +

+ github.com/soloshun/opendsa +

+
+ + + + {/* Right side - FAQ items */} +
+ {faqItems.map((item, index) => ( + + + + {openId === item.id && ( + +
+ {item.answer} +
+
+ )} +
+
+ ))} +
+
+
+
+ ); +} diff --git a/apps/web/src/components/sections/features.tsx b/apps/web/src/components/sections/features.tsx new file mode 100644 index 0000000..f3af6f6 --- /dev/null +++ b/apps/web/src/components/sections/features.tsx @@ -0,0 +1,268 @@ +"use client"; + +import { motion } from "framer-motion"; +import { + BarChart3, + Binary, + Braces, + GitBranch, + Layers, + Play, + Zap, + Keyboard, + Share2, + Sparkles, +} from "lucide-react"; +import { SortingBars } from "@/components/animations/sorting-bars"; +import { BinaryTree } from "@/components/animations/binary-tree"; +import { GraphVisualization } from "@/components/animations/graph-visualization"; +import { CodePreview } from "@/components/animations/code-preview"; +import { ArraySearch } from "@/components/animations/array-search"; +import { AIAssistant } from "@/components/animations/ai-assistant"; +import { KeyboardDemo } from "@/components/animations/keyboard-demo"; + +interface FeatureCardProps { + title: string; + description: string; + icon: React.ReactNode; + children?: React.ReactNode; + className?: string; + index?: number; +} + +function FeatureCard({ title, description, icon, children, className = "", index = 0 }: FeatureCardProps) { + return ( + + {/* Noise overlay */} +
+
+
+ + {/* Hover gradient - diagonal slant */} +
+ + {/* Corner glow on hover - slanted */} +
+ + {/* Content */} +
+ {/* Icon */} +
+ {icon} +
+ + {/* Title */} +

+ {title} +

+ + {/* Description */} +

+ {description} +

+ + {/* Custom content */} + {children &&
{children}
} +
+ + ); +} + +export function Features() { + return ( +
+ {/* Background with gradient */} +
+
+ + {/* Noise overlay */} +
+
+
+ +
+ {/* Section header - left aligned */} + +
+ / Features +
+
+

+ EVERYTHING YOU NEED +
+ 100% FREE +

+

+ Professional visualizations. Zero cost. No catch. +

+ + + {/* Bento Grid - Row 1: Large + 2 small stacked */} +
+ {/* Sorting Algorithms - Large card */} + } + className="lg:col-span-2" + index={0} + > +
+ +
+
+ + {/* Right column with 2 stacked cards */} +
+ {/* Step Control */} + } + index={1} + > +
+ {["Play", "Pause", "Step", "Reset"].map((action) => ( + + {action} + + ))} +
+
+ + {/* Speed Control */} + } + index={2} + > +
+
+ +
+
+ 0.5x + 10x +
+
+
+
+
+ + {/* Row 2: Search full width */} +
+ } + index={3} + > +
+ +
+
+
+ + {/* Row 3: Tree + Keyboard + Graph (3 columns) */} +
+ {/* Tree Structures */} + } + index={4} + > +
+ +
+
+ + {/* Keyboard Shortcuts - with demo animation */} + } + index={5} + > +
+ +
+
+ + {/* Graph Algorithms */} + } + className="md:col-span-2 lg:col-span-1" + index={6} + > +
+ +
+
+
+ + {/* Row 4: AI Assistant + Code Sync (2 columns) */} +
+ {/* AI Assistant */} + } + index={7} + > +
+ +
+
+ + {/* Code Sync */} + } + index={8} + > +
+ +
+
+
+ + {/* Row 5: Shareable URLs full width */} + } + index={9} + > +
+ opendsa.dev/visualize/sorting/bubble?data=5,3,8,1&step=4 +
+
+
+
+ ); +} diff --git a/apps/web/src/components/sections/footer.tsx b/apps/web/src/components/sections/footer.tsx new file mode 100644 index 0000000..14cf254 --- /dev/null +++ b/apps/web/src/components/sections/footer.tsx @@ -0,0 +1,181 @@ +"use client"; + +import { Github, Twitter, Heart } from "lucide-react"; +import Image from "next/image"; +import Link from "next/link"; +import { useEffect, useState } from "react"; + +const footerLinks = { + product: [ + { label: "Features", href: "#features" }, + { label: "Roadmap", href: "#roadmap" }, + { label: "Launch App", href: "https://app.opendsa.dev" }, + ], + resources: [ + { label: "Documentation", href: "https://docs.opendsa.dev" }, + { label: "Contributing", href: "https://github.com/soloshun/opendsa/blob/main/CONTRIBUTING.md" }, + { label: "Changelog", href: "https://github.com/soloshun/opendsa/releases" }, + ], + community: [ + { label: "GitHub", href: "https://github.com/soloshun/opendsa" }, + { label: "Discord", href: "#" }, + { label: "Twitter", href: "#" }, + ], +}; + +export function Footer() { + const [avatarUrl, setAvatarUrl] = useState(null); + + useEffect(() => { + async function fetchAvatar() { + try { + const res = await fetch("https://api.github.com/users/soloshun"); + if (res.ok) { + const data = await res.json(); + setAvatarUrl(data.avatar_url); + } + } catch (error) { + console.error("Failed to fetch avatar:", error); + } + } + fetchAvatar(); + }, []); + + return ( +
+ {/* Gradient from bottom */} +
+
+ + {/* Background */} +
+ + {/* Noise overlay */} +
+
+
+ +
+
+ {/* Brand */} +
+ +
+ + {"<>"} + +
+ + OPENDSA + + +

+ An open-source, interactive platform for visualizing data + structures and algorithms. +

+
+ + + + + + +
+
+ + {/* Product */} +
+

Product

+
    + {footerLinks.product.map((link) => ( +
  • + + {link.label} + +
  • + ))} +
+
+ + {/* Resources */} +
+

Resources

+
    + {footerLinks.resources.map((link) => ( +
  • + + {link.label} + +
  • + ))} +
+
+ + {/* Community */} +
+

Community

+
    + {footerLinks.community.map((link) => ( +
  • + + {link.label} + +
  • + ))} +
+
+
+ + {/* Bottom */} +
+

+ © {new Date().getFullYear()} OpenDSA. MIT License. +

+
+ Made with + + by + + {avatarUrl && ( + Solomon Eshun + )} + Solomon Eshun + +
+
+
+
+ ); +} diff --git a/apps/web/src/components/sections/header.tsx b/apps/web/src/components/sections/header.tsx new file mode 100644 index 0000000..c8fab02 --- /dev/null +++ b/apps/web/src/components/sections/header.tsx @@ -0,0 +1,138 @@ +"use client"; + +import { motion } from "framer-motion"; +import { Github, Menu, X, Sun, Moon } from "lucide-react"; +import Link from "next/link"; +import { useState } from "react"; + +const navLinks = [ + { href: "#features", label: "Features" }, + { href: "#roadmap", label: "Roadmap" }, + { href: "#open-source", label: "Open Source" }, + { href: "https://docs.opendsa.dev", label: "Docs", external: true }, +]; + +export function Header() { + const [mobileMenuOpen, setMobileMenuOpen] = useState(false); + + return ( + + {/* Main nav container - rounded pill style */} + + + {/* Mobile Navigation */} + {mobileMenuOpen && ( + +
+ {navLinks.map((link) => ( + setMobileMenuOpen(false)} + > + {link.label} + + ))} +
+
+ + + GitHub + + + Launch App + +
+
+ )} +
+ ); +} diff --git a/apps/web/src/components/sections/hero.tsx b/apps/web/src/components/sections/hero.tsx new file mode 100644 index 0000000..84eb18c --- /dev/null +++ b/apps/web/src/components/sections/hero.tsx @@ -0,0 +1,147 @@ +"use client"; + +import { motion } from "framer-motion"; +import { ArrowRight, Github, Sparkles } from "lucide-react"; +import Link from "next/link"; + +export function Hero() { + return ( +
+ {/* Background - base layer */} +
+ + {/* Grid pattern - subtle overlay */} +
+ + {/* Noise texture overlay */} +
+
+
+ + {/* Gradient orbs - decorative */} +
+
+ + {/* Main content */} +
+ {/* Centered content wrapper */} +
+ {/* Top badges */} + + + + 100% Open Source + + + + Star on GitHub + + + + {/* Main heading - Mobile responsive and always centered */} + +

+ VISUALIZE +

+
+ + ALGORITHMS + +
+

+ BEAUTIFULLY +

+
+ + {/* Description */} + + The open-source algorithm visualization platform. Interactive visualizations + for sorting, searching, graphs, trees, and more. Learn through{" "} + step-by-step animations. + + + {/* Feature tags */} + + Free forever + · + No sign-up + · + MIT Licensed + + + {/* CTA Buttons */} + + + Start Visualizing + + + + See Features + + +
+
+ + {/* Bottom gradient fade */} +
+ + {/* Scroll indicator - hide on mobile */} + + +
+ +
+
+
+
+ ); +} diff --git a/apps/web/src/components/sections/open-source.tsx b/apps/web/src/components/sections/open-source.tsx new file mode 100644 index 0000000..6d12a06 --- /dev/null +++ b/apps/web/src/components/sections/open-source.tsx @@ -0,0 +1,176 @@ +"use client"; + +import { motion } from "framer-motion"; +import { Github, Heart, Star, GitFork, Users, ExternalLink } from "lucide-react"; +import Link from "next/link"; +import { useEffect, useState } from "react"; + +interface GitHubStats { + stars: number; + forks: number; + contributors: number; +} + +export function OpenSource() { + const [stats, setStats] = useState({ stars: 0, forks: 0, contributors: 1 }); + const [loading, setLoading] = useState(true); + + useEffect(() => { + async function fetchGitHubStats() { + try { + // Fetch repo data + const repoRes = await fetch("https://api.github.com/repos/soloshun/opendsa"); + if (repoRes.ok) { + const repoData = await repoRes.json(); + setStats(prev => ({ + ...prev, + stars: repoData.stargazers_count || 0, + forks: repoData.forks_count || 0, + })); + } + + // Fetch contributors count + const contribRes = await fetch("https://api.github.com/repos/soloshun/opendsa/contributors?per_page=1"); + if (contribRes.ok) { + const linkHeader = contribRes.headers.get("Link"); + if (linkHeader) { + const match = linkHeader.match(/page=(\d+)>; rel="last"/); + if (match) { + setStats(prev => ({ ...prev, contributors: parseInt(match[1]) })); + } + } else { + const data = await contribRes.json(); + setStats(prev => ({ ...prev, contributors: data.length || 1 })); + } + } + } catch (error) { + console.error("Failed to fetch GitHub stats:", error); + } finally { + setLoading(false); + } + } + + fetchGitHubStats(); + }, []); + + return ( +
+ {/* Full-width gradient background */} +
+
+ + {/* Noise overlay */} +
+
+
+ + {/* Grid pattern */} +
+ + {/* Gradient orbs */} +
+ +
+ + {/* Icon */} + + + + + {/* Heading - creative layout */} +

+ BUILT BY THE COMMUNITY +
+ FOR THE COMMUNITY +

+ +

+ OpenDSA is 100% open source and always will be. Join us in building + the best algorithm visualization platform. +

+ + {/* Stats */} +
+ {[ + { icon: , label: "Stars", value: loading ? "..." : stats.stars.toString() }, + { icon: , label: "Forks", value: loading ? "..." : stats.forks.toString() }, + { icon: , label: "Contributors", value: loading ? "..." : stats.contributors.toString() }, + { icon: , label: "Sponsors", value: "0" }, + ].map((stat, index) => ( + +
+
+
+
+
{stat.icon}
+
{stat.value}
+
{stat.label}
+
+ + ))} +
+ + {/* CTAs */} +
+ + + Star on GitHub + + + + Contribute + +
+ + {/* Contribution types */} +
+ {[ + "New Algorithms", + "Bug Fixes", + "Documentation", + "UI/UX Design", + "Testing", + "Translations", + ].map((type) => ( + + {type} + + ))} +
+
+
+
+ ); +} diff --git a/apps/web/src/components/sections/roadmap.tsx b/apps/web/src/components/sections/roadmap.tsx new file mode 100644 index 0000000..e737651 --- /dev/null +++ b/apps/web/src/components/sections/roadmap.tsx @@ -0,0 +1,240 @@ +"use client"; + +import { motion } from "framer-motion"; +import { + Code2, + Compass, + Rocket, + Users, + CheckCircle2, + Circle, +} from "lucide-react"; + +interface RoadmapItem { + phase: string; + title: string; + quarter: string; + status: "completed" | "current" | "upcoming"; + items: string[]; + icon: React.ReactNode; +} + +const roadmapItems: RoadmapItem[] = [ + { + phase: "01", + title: "Foundation", + quarter: "Q1 2026", + status: "current", + icon: , + items: [ + "Turborepo monorepo setup", + "5 sorting algorithms", + "2 searching algorithms", + "Animation engine", + ], + }, + { + phase: "02", + title: "Core Features", + quarter: "Q2 2026", + status: "upcoming", + icon: , + items: [ + "Graph algorithms", + "Tree visualizations", + "Data structures", + "Code editor integration", + ], + }, + { + phase: "03", + title: "Learning", + quarter: "Q3 2026", + status: "upcoming", + icon: , + items: [ + "Learning paths", + "Interactive tutorials", + "Challenge mode", + "Community features", + ], + }, + { + phase: "04", + title: "Advanced", + quarter: "Q4 2026", + status: "upcoming", + icon: , + items: [ + "User accounts", + "Save visualizations", + "Embed widget", + "Public API", + ], + }, +]; + +export function Roadmap() { + return ( +
+ {/* Background with subtle gradient */} +
+
+ + {/* Noise overlay */} +
+
+
+ +
+ {/* Section header - right aligned */} + +
+
+ / Roadmap +
+

+ THE PATH TO{" "} + V1.0 +

+

+ Building in public. Ship fast, iterate faster. +

+ + + {/* Zigzag Timeline */} +
+ {/* Animated connecting line - hidden on mobile */} +
+ +
+ + {/* Timeline items */} +
+ {roadmapItems.map((item, index) => { + const isEven = index % 2 === 0; + + return ( + + {/* Connector dot */} +
+ + {item.status === "current" && ( + + )} +
+ + {/* Card */} +
+ {/* Noise overlay */} +
+
+
+ + {/* Hover gradient */} +
+ +
+ {/* Phase number and status */} +
+ + {item.phase} + + {item.status === "current" ? ( + + + + + + In Progress + + ) : item.status === "completed" ? ( + + ) : ( + + )} +
+ + {/* Icon */} +
+ {item.icon} +
+ + {/* Title and quarter */} +

+ {item.title} +

+

+ {item.quarter} +

+ + {/* Items list */} +
    + {item.items.map((listItem, i) => ( +
  • + + {listItem} +
  • + ))} +
+
+
+ + ); + })} +
+
+
+
+ ); +} diff --git a/apps/web/src/components/sections/tech-stack.tsx b/apps/web/src/components/sections/tech-stack.tsx new file mode 100644 index 0000000..fa0b001 --- /dev/null +++ b/apps/web/src/components/sections/tech-stack.tsx @@ -0,0 +1,148 @@ +"use client"; + +import { motion } from "framer-motion"; +import { Cpu, Zap, Box, Palette, Database, Layers } from "lucide-react"; + +const technologies = [ + { + category: "Framework", + name: "Next.js 14", + description: "App Router & Server Components", + icon: , + highlight: true, + }, + { + category: "Animation", + name: "Framer Motion", + description: "Smooth declarative animations", + icon: , + highlight: true, + }, + { + category: "Visualization", + name: "D3.js + Canvas", + description: "Powerful data visualization", + icon: , + highlight: false, + }, + { + category: "Styling", + name: "TailwindCSS", + description: "Utility-first CSS", + icon: , + highlight: false, + }, + { + category: "State", + name: "Zustand", + description: "Lightweight state", + icon: , + highlight: false, + }, + { + category: "Build", + name: "Turborepo", + description: "High-performance monorepo", + icon: , + highlight: false, + }, +]; + +export function TechStack() { + return ( +
+ {/* Background with accent gradient */} +
+
+
+ + {/* Noise overlay */} +
+
+
+ + {/* Decorative orb */} +
+ +
+
+ {/* Left side - Header */} + + {/* Label */} +
+ + / System Diagnostics +
+ + {/* Heading */} +

+ UNDER THE +
+ HOOD +

+ + {/* Description */} +

+ Built on modern browser capabilities. No servers needed for visualizations. + Everything runs client-side for instant feedback. +

+
+ + {/* Right side - Tech grid */} +
+
+ {technologies.map((tech, index) => ( + + {/* Noise overlay */} +
+
+
+ + {/* Hover effect */} +
+ +
+ {/* Icon */} +
+ {tech.icon} +
+ + + {tech.category} + +

+ {tech.name} +

+

+ {tech.description} +

+
+ + ))} +
+
+
+
+
+ ); +} diff --git a/apps/web/src/components/ui/bento-grid.tsx b/apps/web/src/components/ui/bento-grid.tsx new file mode 100644 index 0000000..36f4c2b --- /dev/null +++ b/apps/web/src/components/ui/bento-grid.tsx @@ -0,0 +1,76 @@ +"use client"; + +import { cn } from "@/lib/utils"; +import { motion } from "framer-motion"; +import { ReactNode } from "react"; + +interface BentoGridProps { + children: ReactNode; + className?: string; +} + +export function BentoGrid({ children, className }: BentoGridProps) { + return ( +
+ {children} +
+ ); +} + +interface BentoCardProps { + title: string; + description: string; + icon?: ReactNode; + className?: string; + children?: ReactNode; + index?: number; +} + +export function BentoCard({ + title, + description, + icon, + className, + children, + index = 0, +}: BentoCardProps) { + return ( + + {/* Gradient overlay on hover */} +
+ + {/* Content */} +
+ {icon && ( +
+ {icon} +
+ )} +

+ {title} +

+

+ {description} +

+ {children &&
{children}
} +
+ + {/* Shimmer effect */} +
+ + ); +} diff --git a/apps/web/src/components/ui/button.tsx b/apps/web/src/components/ui/button.tsx new file mode 100644 index 0000000..ec0694f --- /dev/null +++ b/apps/web/src/components/ui/button.tsx @@ -0,0 +1,97 @@ +"use client"; + +import { cn } from "@/lib/utils"; +import { motion } from "framer-motion"; +import { forwardRef, ReactNode } from "react"; +import Link from "next/link"; + +interface ButtonProps { + variant?: "primary" | "secondary" | "outline" | "ghost"; + size?: "sm" | "md" | "lg"; + className?: string; + children?: ReactNode; + asChild?: boolean; + href?: string; + target?: string; + rel?: string; + onClick?: () => void; + disabled?: boolean; + type?: "button" | "submit" | "reset"; +} + +export const Button = forwardRef( + ( + { + className, + variant = "primary", + size = "md", + children, + asChild, + href, + target, + rel, + onClick, + disabled, + type = "button", + }, + ref + ) => { + const variants = { + primary: + "bg-[hsl(var(--primary))] text-[hsl(var(--primary-foreground))] hover:opacity-90 shadow-lg shadow-[hsl(var(--primary))]/25", + secondary: + "bg-[hsl(var(--secondary))] text-[hsl(var(--secondary-foreground))] hover:bg-[hsl(var(--secondary))]/80", + outline: + "border border-[hsl(var(--border))] bg-transparent hover:bg-[hsl(var(--secondary))] hover:border-[hsl(var(--primary))]/50", + ghost: "bg-transparent hover:bg-[hsl(var(--secondary))]", + }; + + const sizes = { + sm: "h-9 px-4 text-sm", + md: "h-11 px-6 text-sm", + lg: "h-12 px-8 text-base", + }; + + const baseClassName = cn( + "inline-flex items-center justify-center gap-2 rounded-full font-medium transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[hsl(var(--ring))] disabled:pointer-events-none disabled:opacity-50", + variants[variant], + sizes[size], + className + ); + + if (asChild && href) { + return ( + + + {children} + + + ); + } + + return ( + } + whileHover={{ scale: 1.02 }} + whileTap={{ scale: 0.98 }} + className={baseClassName} + onClick={onClick} + disabled={disabled} + type={type} + > + {children} + + ); + } +); + +Button.displayName = "Button"; diff --git a/apps/web/src/components/ui/particles-background.tsx b/apps/web/src/components/ui/particles-background.tsx new file mode 100644 index 0000000..63dc8df --- /dev/null +++ b/apps/web/src/components/ui/particles-background.tsx @@ -0,0 +1,112 @@ +"use client"; + +import { useCallback, useEffect, useMemo, useState } from "react"; +import Particles, { initParticlesEngine } from "@tsparticles/react"; +import { type Container, type ISourceOptions } from "@tsparticles/engine"; +import { loadSlim } from "@tsparticles/slim"; + +export function ParticlesBackground() { + const [init, setInit] = useState(false); + + useEffect(() => { + initParticlesEngine(async (engine) => { + await loadSlim(engine); + }).then(() => { + setInit(true); + }); + }, []); + + const particlesLoaded = useCallback(async (container: Container | undefined) => { + console.log("Particles loaded", container); + }, []); + + const options: ISourceOptions = useMemo( + () => ({ + fullScreen: { + enable: false, + }, + background: { + color: { + value: "transparent", + }, + }, + fpsLimit: 60, + interactivity: { + events: { + onClick: { + enable: true, + mode: "push", + }, + onHover: { + enable: true, + mode: "grab", + }, + }, + modes: { + grab: { + distance: 180, + links: { + opacity: 0.4, + color: "#22c55e", + }, + }, + push: { + quantity: 2, + }, + }, + }, + particles: { + color: { + value: "#22c55e", // Green primary color + }, + links: { + color: "#22c55e", + distance: 180, + enable: true, + opacity: 0.4, // Very subtle links + width: 1, + }, + move: { + direction: "none", + enable: true, + outModes: { + default: "bounce", + }, + random: false, + speed: 0.6, + straight: false, + }, + number: { + density: { + enable: true, + width: 1400, + height: 900, + }, + value: 60, + }, + opacity: { + value: 0.4, // More visible dots + }, + shape: { + type: "circle", + }, + size: { + value: { min: 2, max: 4 }, // Visible dots + }, + }, + detectRetina: true, + }), + [] + ); + + if (!init) return null; + + return ( + + ); +} diff --git a/apps/web/src/lib/utils.ts b/apps/web/src/lib/utils.ts new file mode 100644 index 0000000..365058c --- /dev/null +++ b/apps/web/src/lib/utils.ts @@ -0,0 +1,6 @@ +import { type ClassValue, clsx } from "clsx"; +import { twMerge } from "tailwind-merge"; + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)); +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3f513d4..f2921a1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -63,6 +63,9 @@ importers: apps/docs: dependencies: + '@theguild/remark-mermaid': + specifier: ^0.3.0 + version: 0.3.0(react@19.2.3) next: specifier: 16.1.4 version: 16.1.4(@babel/core@7.28.6)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) @@ -81,6 +84,21 @@ importers: apps/web: dependencies: + '@tsparticles/react': + specifier: ^3.0.0 + version: 3.0.0(@tsparticles/engine@3.9.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@tsparticles/slim': + specifier: ^3.9.1 + version: 3.9.1 + clsx: + specifier: ^2.1.1 + version: 2.1.1 + framer-motion: + specifier: ^12.29.0 + version: 12.29.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + lucide-react: + specifier: ^0.563.0 + version: 0.563.0(react@19.2.3) next: specifier: 16.1.4 version: 16.1.4(@babel/core@7.28.6)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) @@ -90,6 +108,9 @@ importers: react-dom: specifier: 19.2.3 version: 19.2.3(react@19.2.3) + tailwind-merge: + specifier: ^3.4.0 + version: 3.4.0 devDependencies: '@tailwindcss/postcss': specifier: ^4 @@ -826,6 +847,121 @@ packages: '@ts-morph/common@0.28.1': resolution: {integrity: sha512-W74iWf7ILp1ZKNYXY5qbddNaml7e9Sedv5lvU1V8lftlitkc9Pq1A+jlH23ltDgWYeZFFEqGCD1Ies9hqu3O+g==} + '@tsparticles/basic@3.9.1': + resolution: {integrity: sha512-ijr2dHMx0IQHqhKW3qA8tfwrR2XYbbWYdaJMQuBo2CkwBVIhZ76U+H20Y492j/NXpd1FUnt2aC0l4CEVGVGdeQ==} + + '@tsparticles/engine@3.9.1': + resolution: {integrity: sha512-DpdgAhWMZ3Eh2gyxik8FXS6BKZ8vyea+Eu5BC4epsahqTGY9V3JGGJcXC6lRJx6cPMAx1A0FaQAojPF3v6rkmQ==} + + '@tsparticles/interaction-external-attract@3.9.1': + resolution: {integrity: sha512-5AJGmhzM9o4AVFV24WH5vSqMBzOXEOzIdGLIr+QJf4fRh9ZK62snsusv/ozKgs2KteRYQx+L7c5V3TqcDy2upg==} + + '@tsparticles/interaction-external-bounce@3.9.1': + resolution: {integrity: sha512-bv05+h70UIHOTWeTsTI1AeAmX6R3s8nnY74Ea6p6AbQjERzPYIa0XY19nq/hA7+Nrg+EissP5zgoYYeSphr85A==} + + '@tsparticles/interaction-external-bubble@3.9.1': + resolution: {integrity: sha512-tbd8ox/1GPl+zr+KyHQVV1bW88GE7OM6i4zql801YIlCDrl9wgTDdDFGIy9X7/cwTvTrCePhrfvdkUamXIribQ==} + + '@tsparticles/interaction-external-connect@3.9.1': + resolution: {integrity: sha512-sq8YfUNsIORjXHzzW7/AJQtfi/qDqLnYG2qOSE1WOsog39MD30RzmiOloejOkfNeUdcGUcfsDgpUuL3UhzFUOA==} + + '@tsparticles/interaction-external-grab@3.9.1': + resolution: {integrity: sha512-QwXza+sMMWDaMiFxd8y2tJwUK6c+nNw554+/9+tEZeTTk2fCbB0IJ7p/TH6ZGWDL0vo2muK54Njv2fEey191ow==} + + '@tsparticles/interaction-external-pause@3.9.1': + resolution: {integrity: sha512-Gzv4/FeNir0U/tVM9zQCqV1k+IAgaFjDU3T30M1AeAsNGh/rCITV2wnT7TOGFkbcla27m4Yxa+Fuab8+8pzm+g==} + + '@tsparticles/interaction-external-push@3.9.1': + resolution: {integrity: sha512-GvnWF9Qy4YkZdx+WJL2iy9IcgLvzOIu3K7aLYJFsQPaxT8d9TF8WlpoMlWKnJID6H5q4JqQuMRKRyWH8aAKyQw==} + + '@tsparticles/interaction-external-remove@3.9.1': + resolution: {integrity: sha512-yPThm4UDWejDOWW5Qc8KnnS2EfSo5VFcJUQDWc1+Wcj17xe7vdSoiwwOORM0PmNBzdDpSKQrte/gUnoqaUMwOA==} + + '@tsparticles/interaction-external-repulse@3.9.1': + resolution: {integrity: sha512-/LBppXkrMdvLHlEKWC7IykFhzrz+9nebT2fwSSFXK4plEBxDlIwnkDxd3FbVOAbnBvx4+L8+fbrEx+RvC8diAw==} + + '@tsparticles/interaction-external-slow@3.9.1': + resolution: {integrity: sha512-1ZYIR/udBwA9MdSCfgADsbDXKSFS0FMWuPWz7bm79g3sUxcYkihn+/hDhc6GXvNNR46V1ocJjrj0u6pAynS1KQ==} + + '@tsparticles/interaction-particles-attract@3.9.1': + resolution: {integrity: sha512-CYYYowJuGwRLUixQcSU/48PTKM8fCUYThe0hXwQ+yRMLAn053VHzL7NNZzKqEIeEyt5oJoy9KcvubjKWbzMBLQ==} + + '@tsparticles/interaction-particles-collisions@3.9.1': + resolution: {integrity: sha512-ggGyjW/3v1yxvYW1IF1EMT15M6w31y5zfNNUPkqd/IXRNPYvm0Z0ayhp+FKmz70M5p0UxxPIQHTvAv9Jqnuj8w==} + + '@tsparticles/interaction-particles-links@3.9.1': + resolution: {integrity: sha512-MsLbMjy1vY5M5/hu/oa5OSRZAUz49H3+9EBMTIOThiX+a+vpl3sxc9AqNd9gMsPbM4WJlub8T6VBZdyvzez1Vg==} + + '@tsparticles/move-base@3.9.1': + resolution: {integrity: sha512-X4huBS27d8srpxwOxliWPUt+NtCwY+8q/cx1DvQxyqmTA8VFCGpcHNwtqiN+9JicgzOvSuaORVqUgwlsc7h4pQ==} + + '@tsparticles/move-parallax@3.9.1': + resolution: {integrity: sha512-whlOR0bVeyh6J/hvxf/QM3DqvNnITMiAQ0kro6saqSDItAVqg4pYxBfEsSOKq7EhjxNvfhhqR+pFMhp06zoCVA==} + + '@tsparticles/plugin-easing-quad@3.9.1': + resolution: {integrity: sha512-C2UJOca5MTDXKUTBXj30Kiqr5UyID+xrY/LxicVWWZPczQW2bBxbIbfq9ULvzGDwBTxE2rdvIB8YFKmDYO45qw==} + + '@tsparticles/plugin-hex-color@3.9.1': + resolution: {integrity: sha512-vZgZ12AjUicJvk7AX4K2eAmKEQX/D1VEjEPFhyjbgI7A65eX72M465vVKIgNA6QArLZ1DLs7Z787LOE6GOBWsg==} + + '@tsparticles/plugin-hsl-color@3.9.1': + resolution: {integrity: sha512-jJd1iGgRwX6eeNjc1zUXiJivaqC5UE+SC2A3/NtHwwoQrkfxGWmRHOsVyLnOBRcCPgBp/FpdDe6DIDjCMO715w==} + + '@tsparticles/plugin-rgb-color@3.9.1': + resolution: {integrity: sha512-SBxk7f1KBfXeTnnklbE2Hx4jBgh6I6HOtxb+Os1gTp0oaghZOkWcCD2dP4QbUu7fVNCMOcApPoMNC8RTFcy9wQ==} + + '@tsparticles/react@3.0.0': + resolution: {integrity: sha512-hjGEtTT1cwv6BcjL+GcVgH++KYs52bIuQGW3PWv7z3tMa8g0bd6RI/vWSLj7p//NZ3uTjEIeilYIUPBh7Jfq/Q==} + peerDependencies: + '@tsparticles/engine': ^3.0.2 + react: '>=16.8.0' + react-dom: '>=16.8.0' + + '@tsparticles/shape-circle@3.9.1': + resolution: {integrity: sha512-DqZFLjbuhVn99WJ+A9ajz9YON72RtCcvubzq6qfjFmtwAK7frvQeb6iDTp6Ze9FUipluxVZWVRG4vWTxi2B+/g==} + + '@tsparticles/shape-emoji@3.9.1': + resolution: {integrity: sha512-ifvY63usuT+hipgVHb8gelBHSeF6ryPnMxAAEC1RGHhhXfpSRWMtE6ybr+pSsYU52M3G9+TF84v91pSwNrb9ZQ==} + + '@tsparticles/shape-image@3.9.1': + resolution: {integrity: sha512-fCA5eme8VF3oX8yNVUA0l2SLDKuiZObkijb0z3Ky0qj1HUEVlAuEMhhNDNB9E2iELTrWEix9z7BFMePp2CC7AA==} + + '@tsparticles/shape-line@3.9.1': + resolution: {integrity: sha512-wT8NSp0N9HURyV05f371cHKcNTNqr0/cwUu6WhBzbshkYGy1KZUP9CpRIh5FCrBpTev34mEQfOXDycgfG0KiLQ==} + + '@tsparticles/shape-polygon@3.9.1': + resolution: {integrity: sha512-dA77PgZdoLwxnliH6XQM/zF0r4jhT01pw5y7XTeTqws++hg4rTLV9255k6R6eUqKq0FPSW1/WBsBIl7q/MmrqQ==} + + '@tsparticles/shape-square@3.9.1': + resolution: {integrity: sha512-DKGkDnRyZrAm7T2ipqNezJahSWs6xd9O5LQLe5vjrYm1qGwrFxJiQaAdlb00UNrexz1/SA7bEoIg4XKaFa7qhQ==} + + '@tsparticles/shape-star@3.9.1': + resolution: {integrity: sha512-kdMJpi8cdeb6vGrZVSxTG0JIjCwIenggqk0EYeKAwtOGZFBgL7eHhF2F6uu1oq8cJAbXPujEoabnLsz6mW8XaA==} + + '@tsparticles/slim@3.9.1': + resolution: {integrity: sha512-CL5cDmADU7sDjRli0So+hY61VMbdroqbArmR9Av+c1Fisa5ytr6QD7Jv62iwU2S6rvgicEe9OyRmSy5GIefwZw==} + + '@tsparticles/updater-color@3.9.1': + resolution: {integrity: sha512-XGWdscrgEMA8L5E7exsE0f8/2zHKIqnTrZymcyuFBw2DCB6BIV+5z6qaNStpxrhq3DbIxxhqqcybqeOo7+Alpg==} + + '@tsparticles/updater-life@3.9.1': + resolution: {integrity: sha512-Oi8aF2RIwMMsjssUkCB6t3PRpENHjdZf6cX92WNfAuqXtQphr3OMAkYFJFWkvyPFK22AVy3p/cFt6KE5zXxwAA==} + + '@tsparticles/updater-opacity@3.9.1': + resolution: {integrity: sha512-w778LQuRZJ+IoWzeRdrGykPYSSaTeWfBvLZ2XwYEkh/Ss961InOxZKIpcS6i5Kp/Zfw0fS1ZAuqeHwuj///Osw==} + + '@tsparticles/updater-out-modes@3.9.1': + resolution: {integrity: sha512-cKQEkAwbru+hhKF+GTsfbOvuBbx2DSB25CxOdhtW2wRvDBoCnngNdLw91rs+0Cex4tgEeibkebrIKFDDE6kELg==} + + '@tsparticles/updater-rotate@3.9.1': + resolution: {integrity: sha512-9BfKaGfp28JN82MF2qs6Ae/lJr9EColMfMTHqSKljblwbpVDHte4umuwKl3VjbRt87WD9MGtla66NTUYl+WxuQ==} + + '@tsparticles/updater-size@3.9.1': + resolution: {integrity: sha512-3NSVs0O2ApNKZXfd+y/zNhTXSFeG1Pw4peI8e6z/q5+XLbmue9oiEwoPy/tQLaark3oNj3JU7Q903ZijPyXSzw==} + + '@tsparticles/updater-stroke-color@3.9.1': + resolution: {integrity: sha512-3x14+C2is9pZYTg9T2TiA/aM1YMq4wLdYaZDcHm3qO30DZu5oeQq0rm/6w+QOGKYY1Z3Htg9rlSUZkhTHn7eDA==} + '@tybys/wasm-util@0.10.1': resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} @@ -1917,6 +2053,20 @@ packages: resolution: {integrity: sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==} engines: {node: '>=0.4.x'} + framer-motion@12.29.0: + resolution: {integrity: sha512-1gEFGXHYV2BD42ZPTFmSU9buehppU+bCuOnHU0AD18DKh9j4DuTx47MvqY5ax+NNWRtK32qIcJf1UxKo1WwjWg==} + peerDependencies: + '@emotion/is-prop-valid': '*' + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@emotion/is-prop-valid': + optional: true + react: + optional: true + react-dom: + optional: true + function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} @@ -2445,6 +2595,11 @@ packages: lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + lucide-react@0.563.0: + resolution: {integrity: sha512-8dXPB2GI4dI8jV4MgUDGBeLdGk8ekfqVZ0BdLcrRzocGgG75ltNEmWS+gE7uokKF/0oSUuczNDT+g9hFJ23FkA==} + peerDependencies: + react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 + magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} @@ -2678,6 +2833,12 @@ packages: mlly@1.8.0: resolution: {integrity: sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==} + motion-dom@12.29.0: + resolution: {integrity: sha512-3eiz9bb32yvY8Q6XNM4AwkSOBPgU//EIKTZwsSWgA9uzbPBhZJeScCVcBuwwYVqhfamewpv7ZNmVKTGp5qnzkA==} + + motion-utils@12.27.2: + resolution: {integrity: sha512-B55gcoL85Mcdt2IEStY5EEAsrMSVE2sI14xQ/uAdPL+mfQxhKKFaEag9JmfxedJOR4vZpBGoPeC/Gm13I/4g5Q==} + ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} @@ -3278,6 +3439,9 @@ packages: tabbable@6.4.0: resolution: {integrity: sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg==} + tailwind-merge@3.4.0: + resolution: {integrity: sha512-uSaO4gnW+b3Y2aWoWfFpX62vn2sR3skfhbjsEnaBI81WD1wBLlHZe5sWf0AqjksNdYTbGBEd0UasQMT3SNV15g==} + tailwindcss@4.1.18: resolution: {integrity: sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==} @@ -4295,6 +4459,188 @@ snapshots: path-browserify: 1.0.1 tinyglobby: 0.2.15 + '@tsparticles/basic@3.9.1': + dependencies: + '@tsparticles/engine': 3.9.1 + '@tsparticles/move-base': 3.9.1 + '@tsparticles/plugin-hex-color': 3.9.1 + '@tsparticles/plugin-hsl-color': 3.9.1 + '@tsparticles/plugin-rgb-color': 3.9.1 + '@tsparticles/shape-circle': 3.9.1 + '@tsparticles/updater-color': 3.9.1 + '@tsparticles/updater-opacity': 3.9.1 + '@tsparticles/updater-out-modes': 3.9.1 + '@tsparticles/updater-size': 3.9.1 + + '@tsparticles/engine@3.9.1': {} + + '@tsparticles/interaction-external-attract@3.9.1': + dependencies: + '@tsparticles/engine': 3.9.1 + + '@tsparticles/interaction-external-bounce@3.9.1': + dependencies: + '@tsparticles/engine': 3.9.1 + + '@tsparticles/interaction-external-bubble@3.9.1': + dependencies: + '@tsparticles/engine': 3.9.1 + + '@tsparticles/interaction-external-connect@3.9.1': + dependencies: + '@tsparticles/engine': 3.9.1 + + '@tsparticles/interaction-external-grab@3.9.1': + dependencies: + '@tsparticles/engine': 3.9.1 + + '@tsparticles/interaction-external-pause@3.9.1': + dependencies: + '@tsparticles/engine': 3.9.1 + + '@tsparticles/interaction-external-push@3.9.1': + dependencies: + '@tsparticles/engine': 3.9.1 + + '@tsparticles/interaction-external-remove@3.9.1': + dependencies: + '@tsparticles/engine': 3.9.1 + + '@tsparticles/interaction-external-repulse@3.9.1': + dependencies: + '@tsparticles/engine': 3.9.1 + + '@tsparticles/interaction-external-slow@3.9.1': + dependencies: + '@tsparticles/engine': 3.9.1 + + '@tsparticles/interaction-particles-attract@3.9.1': + dependencies: + '@tsparticles/engine': 3.9.1 + + '@tsparticles/interaction-particles-collisions@3.9.1': + dependencies: + '@tsparticles/engine': 3.9.1 + + '@tsparticles/interaction-particles-links@3.9.1': + dependencies: + '@tsparticles/engine': 3.9.1 + + '@tsparticles/move-base@3.9.1': + dependencies: + '@tsparticles/engine': 3.9.1 + + '@tsparticles/move-parallax@3.9.1': + dependencies: + '@tsparticles/engine': 3.9.1 + + '@tsparticles/plugin-easing-quad@3.9.1': + dependencies: + '@tsparticles/engine': 3.9.1 + + '@tsparticles/plugin-hex-color@3.9.1': + dependencies: + '@tsparticles/engine': 3.9.1 + + '@tsparticles/plugin-hsl-color@3.9.1': + dependencies: + '@tsparticles/engine': 3.9.1 + + '@tsparticles/plugin-rgb-color@3.9.1': + dependencies: + '@tsparticles/engine': 3.9.1 + + '@tsparticles/react@3.0.0(@tsparticles/engine@3.9.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@tsparticles/engine': 3.9.1 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@tsparticles/shape-circle@3.9.1': + dependencies: + '@tsparticles/engine': 3.9.1 + + '@tsparticles/shape-emoji@3.9.1': + dependencies: + '@tsparticles/engine': 3.9.1 + + '@tsparticles/shape-image@3.9.1': + dependencies: + '@tsparticles/engine': 3.9.1 + + '@tsparticles/shape-line@3.9.1': + dependencies: + '@tsparticles/engine': 3.9.1 + + '@tsparticles/shape-polygon@3.9.1': + dependencies: + '@tsparticles/engine': 3.9.1 + + '@tsparticles/shape-square@3.9.1': + dependencies: + '@tsparticles/engine': 3.9.1 + + '@tsparticles/shape-star@3.9.1': + dependencies: + '@tsparticles/engine': 3.9.1 + + '@tsparticles/slim@3.9.1': + dependencies: + '@tsparticles/basic': 3.9.1 + '@tsparticles/engine': 3.9.1 + '@tsparticles/interaction-external-attract': 3.9.1 + '@tsparticles/interaction-external-bounce': 3.9.1 + '@tsparticles/interaction-external-bubble': 3.9.1 + '@tsparticles/interaction-external-connect': 3.9.1 + '@tsparticles/interaction-external-grab': 3.9.1 + '@tsparticles/interaction-external-pause': 3.9.1 + '@tsparticles/interaction-external-push': 3.9.1 + '@tsparticles/interaction-external-remove': 3.9.1 + '@tsparticles/interaction-external-repulse': 3.9.1 + '@tsparticles/interaction-external-slow': 3.9.1 + '@tsparticles/interaction-particles-attract': 3.9.1 + '@tsparticles/interaction-particles-collisions': 3.9.1 + '@tsparticles/interaction-particles-links': 3.9.1 + '@tsparticles/move-parallax': 3.9.1 + '@tsparticles/plugin-easing-quad': 3.9.1 + '@tsparticles/shape-emoji': 3.9.1 + '@tsparticles/shape-image': 3.9.1 + '@tsparticles/shape-line': 3.9.1 + '@tsparticles/shape-polygon': 3.9.1 + '@tsparticles/shape-square': 3.9.1 + '@tsparticles/shape-star': 3.9.1 + '@tsparticles/updater-life': 3.9.1 + '@tsparticles/updater-rotate': 3.9.1 + '@tsparticles/updater-stroke-color': 3.9.1 + + '@tsparticles/updater-color@3.9.1': + dependencies: + '@tsparticles/engine': 3.9.1 + + '@tsparticles/updater-life@3.9.1': + dependencies: + '@tsparticles/engine': 3.9.1 + + '@tsparticles/updater-opacity@3.9.1': + dependencies: + '@tsparticles/engine': 3.9.1 + + '@tsparticles/updater-out-modes@3.9.1': + dependencies: + '@tsparticles/engine': 3.9.1 + + '@tsparticles/updater-rotate@3.9.1': + dependencies: + '@tsparticles/engine': 3.9.1 + + '@tsparticles/updater-size@3.9.1': + dependencies: + '@tsparticles/engine': 3.9.1 + + '@tsparticles/updater-stroke-color@3.9.1': + dependencies: + '@tsparticles/engine': 3.9.1 + '@tybys/wasm-util@0.10.1': dependencies: tslib: 2.8.1 @@ -5655,6 +6001,15 @@ snapshots: format@0.2.2: {} + framer-motion@12.29.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3): + dependencies: + motion-dom: 12.29.0 + motion-utils: 12.27.2 + tslib: 2.8.1 + optionalDependencies: + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + function-bind@1.1.2: {} function.prototype.name@1.1.8: @@ -6251,6 +6606,10 @@ snapshots: dependencies: yallist: 3.1.1 + lucide-react@0.563.0(react@19.2.3): + dependencies: + react: 19.2.3 + magic-string@0.30.21: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -6798,6 +7157,12 @@ snapshots: pkg-types: 1.3.1 ufo: 1.6.3 + motion-dom@12.29.0: + dependencies: + motion-utils: 12.27.2 + + motion-utils@12.27.2: {} + ms@2.1.3: {} nano-spawn@2.0.0: {} @@ -7616,6 +7981,8 @@ snapshots: tabbable@6.4.0: {} + tailwind-merge@3.4.0: {} + tailwindcss@4.1.18: {} tapable@2.3.0: {} From 3133b7c140d40ac048e8cf1b62a24c6d588e4b0f Mon Sep 17 00:00:00 2001 From: Solo Shun <88045720+soloshun@users.noreply.github.com> Date: Sun, 25 Jan 2026 05:39:34 +0000 Subject: [PATCH 2/7] fix(ci-build-error): lint error in ci build --- .../components/animations/ai-assistant.tsx | 25 +++++++++---------- .../animations/architecture-flow.tsx | 23 +++++------------ .../components/animations/array-search.tsx | 16 ++++++------ apps/web/src/components/sections/hero.tsx | 4 +-- 4 files changed, 28 insertions(+), 40 deletions(-) diff --git a/apps/web/src/components/animations/ai-assistant.tsx b/apps/web/src/components/animations/ai-assistant.tsx index 98d36e5..03125c0 100644 --- a/apps/web/src/components/animations/ai-assistant.tsx +++ b/apps/web/src/components/animations/ai-assistant.tsx @@ -4,15 +4,15 @@ import { motion } from "framer-motion"; import { useEffect, useState } from "react"; import { Sparkles } from "lucide-react"; +const steps = [ + { prompt: "Explain bubble sort", response: "Bubble sort compares adjacent elements..." }, + { prompt: "Optimize this code", response: "Use quick sort for O(n log n)..." }, + { prompt: "Show me BFS", response: "Starting BFS from node A..." }, +]; + export function AIAssistant() { const [currentStep, setCurrentStep] = useState(0); const [displayText, setDisplayText] = useState(""); - - const steps = [ - { prompt: "Explain bubble sort", response: "Bubble sort compares adjacent elements..." }, - { prompt: "Optimize this code", response: "Use quick sort for O(n log n)..." }, - { prompt: "Show me BFS", response: "Starting BFS from node A..." }, - ]; useEffect(() => { const currentResponse = steps[currentStep].response; @@ -77,14 +77,13 @@ export function AIAssistant() {
{/* Editor tabs */}
- {["JS", "PY", "C++"].map((lang, i) => ( + {["JS", "PY", "C++", "JAVA"].map((lang, i) => ( {lang} @@ -93,7 +92,7 @@ export function AIAssistant() { {/* Mini code */}
function sort(arr) {"{"}
-
// AI optimized
+
{`//`} AI optimized
...
{"}"}
diff --git a/apps/web/src/components/animations/architecture-flow.tsx b/apps/web/src/components/animations/architecture-flow.tsx index 3b68787..e47421e 100644 --- a/apps/web/src/components/animations/architecture-flow.tsx +++ b/apps/web/src/components/animations/architecture-flow.tsx @@ -12,19 +12,9 @@ const plugins = [ { id: "bfs", label: "BFSPlugin", x: 50, y: 80 }, ]; -// Registry nodes -const registryNodes = [ - { id: "register", label: "register", x: 60, y: 50 }, - { id: "get", label: "get", x: 40, y: 70 }, - { id: "getByCategory", label: "getByCategory", x: 60, y: 70 }, - { id: "getAll", label: "getAll", x: 80, y: 70 }, -]; -// App nodes -const appNodes = [ - { id: "router", label: "Dynamic Router", x: 20, y: 50 }, - { id: "renderer", label: "Visualizer Renderer", x: 20, y: 85 }, -]; + + export function ArchitectureFlow() { const [activePlugin, setActivePlugin] = useState(0); @@ -231,11 +221,10 @@ export function ArchitectureFlow() { {[0, 1, 2, 3, 4].map((step) => (
))}
diff --git a/apps/web/src/components/animations/array-search.tsx b/apps/web/src/components/animations/array-search.tsx index 6624f80..0285ed0 100644 --- a/apps/web/src/components/animations/array-search.tsx +++ b/apps/web/src/components/animations/array-search.tsx @@ -3,8 +3,9 @@ import { motion } from "framer-motion"; import { useEffect, useState } from "react"; +const array = [2, 5, 8, 11, 12, 16, 23, 28, 38, 56, 60, 67, 72, 81, 91, 99, 123, 212]; + export function ArraySearch() { - const array = [2, 5, 8, 11, 12, 16, 23, 28, 38, 56, 60, 67, 72, 81, 91, 99, 123, 212]; const target = 67; const [currentIndex, setCurrentIndex] = useState(-1); const [left, setLeft] = useState(0); @@ -58,15 +59,14 @@ export function ArraySearch() { return ( {/* Background - base layer */}
- + {/* Grid pattern - subtle overlay */}
@@ -110,7 +110,7 @@ export function Hero() { See Features From 5353611eaf301b1e0fd15f57eb5b627fb1f3d1d7 Mon Sep 17 00:00:00 2001 From: Solo Shun <88045720+soloshun@users.noreply.github.com> Date: Sun, 25 Jan 2026 05:41:49 +0000 Subject: [PATCH 3/7] fix(ci-build-error): remove unsed Moon import from lucid-react --- apps/web/src/components/sections/header.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/web/src/components/sections/header.tsx b/apps/web/src/components/sections/header.tsx index c8fab02..59b70c7 100644 --- a/apps/web/src/components/sections/header.tsx +++ b/apps/web/src/components/sections/header.tsx @@ -1,7 +1,7 @@ "use client"; import { motion } from "framer-motion"; -import { Github, Menu, X, Sun, Moon } from "lucide-react"; +import { Github, Menu, X, Sun } from "lucide-react"; import Link from "next/link"; import { useState } from "react"; From 5e5b89d15047e61fc6fb3b1b6befbd078126dd72 Mon Sep 17 00:00:00 2001 From: Solo Shun <88045720+soloshun@users.noreply.github.com> Date: Sun, 25 Jan 2026 05:43:50 +0000 Subject: [PATCH 4/7] feat: add copy shareable links to clipboard under features card --- apps/web/src/components/sections/features.tsx | 72 +++++++++++++++++-- 1 file changed, 66 insertions(+), 6 deletions(-) diff --git a/apps/web/src/components/sections/features.tsx b/apps/web/src/components/sections/features.tsx index f3af6f6..5345452 100644 --- a/apps/web/src/components/sections/features.tsx +++ b/apps/web/src/components/sections/features.tsx @@ -1,5 +1,7 @@ "use client"; +import React from "react"; + import { motion } from "framer-motion"; import { BarChart3, @@ -12,7 +14,10 @@ import { Keyboard, Share2, Sparkles, + ClipboardCopy, + Check } from "lucide-react"; +import { AnimatePresence } from "framer-motion"; import { SortingBars } from "@/components/animations/sorting-bars"; import { BinaryTree } from "@/components/animations/binary-tree"; import { GraphVisualization } from "@/components/animations/graph-visualization"; @@ -30,6 +35,60 @@ interface FeatureCardProps { index?: number; } +function CopyButton({ text }: { text: string }) { + const [copied, setCopied] = React.useState(false); + + const handleCopy = async () => { + try { + await navigator.clipboard.writeText(text); + setCopied(true); + setTimeout(() => setCopied(false), 2000); + } catch (err) { + console.error("Failed to copy:", err); + } + }; + + return ( +
+ +
+ ); +} + function FeatureCard({ title, description, icon, children, className = "", index = 0 }: FeatureCardProps) { return ( - + {/* Corner glow on hover - slanted */}
@@ -56,12 +115,12 @@ function FeatureCard({ title, description, icon, children, className = "", index
{icon}
- + {/* Title */}

{title}

- + {/* Description */}

{description} @@ -80,7 +139,7 @@ export function Features() { {/* Background with gradient */}

- + {/* Noise overlay */}
@@ -258,8 +317,9 @@ export function Features() { icon={} index={9} > -
- opendsa.dev/visualize/sorting/bubble?data=5,3,8,1&step=4 +
+ opendsa.dev/visualize/sorting/bubble?data=5,3,8,1&step=4 +
From cdb42fe9b7c362e08485ed9c23fca3e9fede85db Mon Sep 17 00:00:00 2001 From: Solo Shun <88045720+soloshun@users.noreply.github.com> Date: Sun, 25 Jan 2026 05:56:02 +0000 Subject: [PATCH 5/7] fix: ci build error `Cannot find module '@tsparticles/engine` --- apps/web/package.json | 1 + pnpm-lock.yaml | 7 +++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/apps/web/package.json b/apps/web/package.json index 1db7ac8..b0edab0 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -9,6 +9,7 @@ "lint": "eslint" }, "dependencies": { + "@tsparticles/engine": "^3.9.1", "@tsparticles/react": "^3.0.0", "@tsparticles/slim": "^3.9.1", "clsx": "^2.1.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f2921a1..99b5866 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -53,7 +53,7 @@ importers: version: 9.39.2(jiti@2.6.1) eslint-config-next: specifier: 16.1.4 - version: 16.1.4(@typescript-eslint/parser@8.53.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + version: 16.1.4(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) tailwindcss: specifier: ^4 version: 4.1.18 @@ -84,6 +84,9 @@ importers: apps/web: dependencies: + '@tsparticles/engine': + specifier: ^3.9.1 + version: 3.9.1 '@tsparticles/react': specifier: ^3.0.0 version: 3.0.0(@tsparticles/engine@3.9.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) @@ -129,7 +132,7 @@ importers: version: 9.39.2(jiti@2.6.1) eslint-config-next: specifier: 16.1.4 - version: 16.1.4(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + version: 16.1.4(@typescript-eslint/parser@8.53.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) tailwindcss: specifier: ^4 version: 4.1.18 From b121e6e1ec9a18d4e0b7de4307d981d4519f4bb8 Mon Sep 17 00:00:00 2001 From: Solo Shun <88045720+soloshun@users.noreply.github.com> Date: Sun, 25 Jan 2026 15:00:27 +0000 Subject: [PATCH 6/7] feat: setup and add documentation application --- README.md | 3 +- apps/docs/app/_meta.js | 12 +- apps/docs/app/architecture/page.mdx | 6 - apps/docs/app/code_of_conduct/page.mdx | 134 +++ apps/docs/app/contributing/page.mdx | 1017 ++++++++++++++++ apps/docs/app/globals.css | 33 + apps/docs/app/layout.jsx | 29 +- apps/docs/app/migration/page.mdx | 6 - apps/docs/app/page.mdx | 107 +- apps/docs/app/project/_meta.js | 6 + apps/docs/app/project/architecture/page.mdx | 948 +++++++++++++++ apps/docs/app/project/migration/page.mdx | 42 + apps/docs/app/project/roadmap/page.mdx | 581 +++++++++ apps/docs/app/project/tech_stack/page.mdx | 1049 +++++++++++++++++ apps/docs/app/roadmap/page.mdx | 6 - apps/docs/app/sponsors/page.mdx | 117 ++ apps/docs/app/tech_stack/page.mdx | 6 - apps/docs/app/technical/_meta.js | 7 + apps/docs/app/technical/algorithms/_meta.js | 4 + .../technical/algorithms/bubble-sort/page.mdx | 28 + apps/docs/app/technical/algorithms/page.mdx | 8 + .../technical/algorithms/quick-sort/page.mdx | 17 + apps/docs/app/technical/big-o/page.mdx | 51 + .../app/technical/data-structures/_meta.js | 4 + .../technical/data-structures/arrays/page.mdx | 18 + .../app/technical/data-structures/page.mdx | 8 + .../technical/data-structures/trees/page.mdx | 12 + .../technical/implementation-status/page.mdx | 185 +++ apps/docs/app/technical/page.mdx | 9 + apps/docs/app/technical/visualizers/_meta.js | 4 + .../visualizers/architecture/page.mdx | 25 + apps/docs/app/technical/visualizers/page.mdx | 7 + ...RCHITECTURE.md => OPENDSA_ARCHITECTURE.md} | 0 ...ALGO_MIGRATION.md => OPENDSA_MIGRATION.md} | 0 ...OPENALGO_ROADMAP.md => OPENDSA_ROADMAP.md} | 0 ...GO_TECH_STACK.md => OPENDSA_TECH_STACK.md} | 0 apps/docs/next.config.mjs | 3 + apps/docs/package.json | 8 +- pnpm-lock.yaml | 7 +- 39 files changed, 4463 insertions(+), 44 deletions(-) delete mode 100644 apps/docs/app/architecture/page.mdx create mode 100644 apps/docs/app/code_of_conduct/page.mdx create mode 100644 apps/docs/app/contributing/page.mdx create mode 100644 apps/docs/app/globals.css delete mode 100644 apps/docs/app/migration/page.mdx create mode 100644 apps/docs/app/project/_meta.js create mode 100644 apps/docs/app/project/architecture/page.mdx create mode 100644 apps/docs/app/project/migration/page.mdx create mode 100644 apps/docs/app/project/roadmap/page.mdx create mode 100644 apps/docs/app/project/tech_stack/page.mdx delete mode 100644 apps/docs/app/roadmap/page.mdx create mode 100644 apps/docs/app/sponsors/page.mdx delete mode 100644 apps/docs/app/tech_stack/page.mdx create mode 100644 apps/docs/app/technical/_meta.js create mode 100644 apps/docs/app/technical/algorithms/_meta.js create mode 100644 apps/docs/app/technical/algorithms/bubble-sort/page.mdx create mode 100644 apps/docs/app/technical/algorithms/page.mdx create mode 100644 apps/docs/app/technical/algorithms/quick-sort/page.mdx create mode 100644 apps/docs/app/technical/big-o/page.mdx create mode 100644 apps/docs/app/technical/data-structures/_meta.js create mode 100644 apps/docs/app/technical/data-structures/arrays/page.mdx create mode 100644 apps/docs/app/technical/data-structures/page.mdx create mode 100644 apps/docs/app/technical/data-structures/trees/page.mdx create mode 100644 apps/docs/app/technical/implementation-status/page.mdx create mode 100644 apps/docs/app/technical/page.mdx create mode 100644 apps/docs/app/technical/visualizers/_meta.js create mode 100644 apps/docs/app/technical/visualizers/architecture/page.mdx create mode 100644 apps/docs/app/technical/visualizers/page.mdx rename apps/docs/internal/{OPENALGO_ARCHITECTURE.md => OPENDSA_ARCHITECTURE.md} (100%) rename apps/docs/internal/{OPENALGO_MIGRATION.md => OPENDSA_MIGRATION.md} (100%) rename apps/docs/internal/{OPENALGO_ROADMAP.md => OPENDSA_ROADMAP.md} (100%) rename apps/docs/internal/{OPENALGO_TECH_STACK.md => OPENDSA_TECH_STACK.md} (100%) diff --git a/README.md b/README.md index 2f9c983..d8ec94a 100644 --- a/README.md +++ b/README.md @@ -222,10 +222,9 @@ If you find OpenDSA useful, consider: OpenDSA is [MIT licensed](LICENSE). ----

- Made with love by the OpenDSA community + Made with love by the Solo Shun for the OpenDSA community

diff --git a/apps/docs/app/_meta.js b/apps/docs/app/_meta.js index fb3c43b..fc947e9 100644 --- a/apps/docs/app/_meta.js +++ b/apps/docs/app/_meta.js @@ -1,8 +1,8 @@ export default { - index: 'Home', - architecture: 'Architecture', - roadmap: 'Roadmap', - migration: 'Migration', - tech_stack: 'Tech Stack' + index: 'Overview', + project: 'Project Overview', + technical: 'Technical Docs', + code_of_conduct: 'Code of Conduct', + contributing: 'Contributing', + sponsors: 'Support Us☕' } - diff --git a/apps/docs/app/architecture/page.mdx b/apps/docs/app/architecture/page.mdx deleted file mode 100644 index c0c5551..0000000 --- a/apps/docs/app/architecture/page.mdx +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: Architecture ---- - -This page will be migrated from `apps/docs/internal/OPENALGO_ARCHITECTURE.md`. - diff --git a/apps/docs/app/code_of_conduct/page.mdx b/apps/docs/app/code_of_conduct/page.mdx new file mode 100644 index 0000000..c9908c8 --- /dev/null +++ b/apps/docs/app/code_of_conduct/page.mdx @@ -0,0 +1,134 @@ +--- +title: Code of Conduct +description: Our pledge and standards for community participation. +--- + +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, religion, or sexual identity +and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or + advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email + address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +**conduct@opendsa.dev**. + +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series +of actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or +permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within +the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.0, available at +https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. + +Community Impact Guidelines were inspired by [Mozilla's code of conduct +enforcement ladder](https://github.com/mozilla/diversity). + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see the FAQ at +https://www.contributor-covenant.org/faq. Translations are available at +https://www.contributor-covenant.org/translations. diff --git a/apps/docs/app/contributing/page.mdx b/apps/docs/app/contributing/page.mdx new file mode 100644 index 0000000..1985a28 --- /dev/null +++ b/apps/docs/app/contributing/page.mdx @@ -0,0 +1,1017 @@ +--- +title: Contributing +description: Guidelines and instructions for contributing to the OpenDSA project. +--- + +# Contributing to OpenDSA + +Thank you for your interest in contributing to OpenDSA! This document provides guidelines and instructions for contributing to the project. + +## Table of Contents + +1. [Code of Conduct](#code-of-conduct) +2. [Getting Started](#getting-started) +3. [Development Setup](#development-setup) +4. [Project Structure](#project-structure) +5. [Making Contributions](#making-contributions) +6. [Adding a New Visualizer](#adding-a-new-visualizer) +7. [Code Style Guide](#code-style-guide) +8. [Commit Guidelines](#commit-guidelines) +9. [Pull Request Process](#pull-request-process) +10. [Issue Guidelines](#issue-guidelines) +11. [Community](#community) + + +## Code of Conduct + +By participating in this project, you agree to abide by our [Code of Conduct](/code_of_conduct). Please read it before contributing. + +**Key Points**: +- Be respectful and inclusive +- Welcome newcomers +- Accept constructive criticism +- Focus on what's best for the community + + +## Getting Started + +### Prerequisites + +Before you begin, ensure you have the following installed: + +- **Node.js**: v18.17 or higher ([Download](https://nodejs.org/)) +- **pnpm**: v8 or higher (`npm install -g pnpm`) +- **Git**: Latest version ([Download](https://git-scm.com/)) + +### Quick Start + +```bash +# 1. Fork the repository on GitHub + +# 2. Clone your fork +git clone https://github.com/soloshun/opendsa.git +cd opendsa + +# 3. Add upstream remote +git remote add upstream https://github.com/soloshun/opendsa.git + +# 4. Install dependencies +pnpm install + +# 5. Start development server +pnpm dev +``` + + +## Development Setup + +### Installing Dependencies + +```bash +# Install all dependencies for the monorepo +pnpm install +``` + +### Running Development Servers + +```bash +# Run all apps in development mode +pnpm dev + +# Run specific app +pnpm dev --filter=@opendsa/app +pnpm dev --filter=@opendsa/web +pnpm dev --filter=@opendsa/docs +``` + +### Building + +```bash +# Build all packages and apps +pnpm build + +# Build specific package +pnpm build --filter=@opendsa/algorithms +``` + +### Testing + +```bash +# Run all tests +pnpm test + +# Run tests in watch mode +pnpm test:watch + +# Run tests for specific package +pnpm test --filter=@opendsa/algorithms + +# Run E2E tests +pnpm test:e2e +``` + +### Linting and Formatting + +```bash +# Run linter +pnpm lint + +# Fix lint issues +pnpm lint:fix + +# Format code +pnpm format + +# Type check +pnpm type-check +``` + + +## Project Structure + +``` +opendsa/ +├── apps/ +│ ├── web/ # Marketing website +│ ├── app/ # Main visualizer application +│ └── docs/ # Documentation site +│ +├── packages/ +│ ├── ui/ # Shared UI components +│ ├── algorithms/ # Algorithm implementations +│ ├── visualizers/ # Visualization components +│ ├── types/ # Shared TypeScript types +│ ├── utils/ # Shared utilities +│ └── config/ # Shared configurations +│ +├── .github/ # GitHub workflows and templates +├── turbo.json # Turborepo configuration +└── package.json # Root package.json +``` + +### Package Overview + +| Package | Description | Key Files | +|---------|-------------|-----------| +| `@opendsa/app` | Main visualizer app | `app/`, `components/` | +| `@opendsa/web` | Marketing website | `app/`, `components/` | +| `@opendsa/docs` | Documentation | `pages/` | +| `@opendsa/ui` | UI components | `src/components/` | +| `@opendsa/algorithms` | Pure algorithms | `src/sorting/`, `src/searching/` | +| `@opendsa/visualizers` | Visualizer components | `src/engine/`, `src/sorting/` | +| `@opendsa/types` | TypeScript types | `src/*.types.ts` | +| `@opendsa/utils` | Utility functions | `src/` | + + +## Making Contributions + +### Types of Contributions + +We welcome many types of contributions: + +- **Bug Fixes**: Fix issues in existing code +- **New Features**: Add new functionality +- **New Visualizers**: Add algorithm visualizations +- **Documentation**: Improve or add documentation +- **Tests**: Add or improve tests +- **UI/UX**: Improve user interface and experience +- **Performance**: Optimize existing code +- **Accessibility**: Improve accessibility + +### Contribution Workflow + +```mermaid +flowchart TB + A[Find or Create Issue] --> B[Fork Repository] + B --> C[Create Feature Branch] + C --> D[Make Changes] + D --> E[Write/Update Tests] + E --> F[Commit with Conventional Commits] + F --> G[Push to Your Fork] + G --> H[Create Pull Request] + H --> I{CI Passes?} + I -->|No| D + I -->|Yes| J[Code Review] + J --> K{Changes Requested?} + K -->|Yes| D + K -->|No| L[Merge] +``` + +### Branch Naming + +Use descriptive branch names: + +``` +feature/add-quicksort-visualizer +fix/bubble-sort-animation-bug +docs/improve-contributing-guide +refactor/animation-engine +test/add-merge-sort-tests +``` + +Format: `/` + +Types: +- `feature/` - New features +- `fix/` - Bug fixes +- `docs/` - Documentation changes +- `refactor/` - Code refactoring +- `test/` - Test additions/changes +- `chore/` - Maintenance tasks + + +## Adding a New Visualizer + +This is the most common contribution. Follow this guide to add a new algorithm visualizer. + +### Step 1: Create the Algorithm Implementation + +Create the pure algorithm in `packages/algorithms`: + +```typescript +// packages/algorithms/src/sorting/insertion-sort.ts + +import type { AnimationStep } from '@opendsa/types'; + +/** + * Insertion Sort Algorithm + * Time: O(n^2) average, O(n) best | Space: O(1) + */ +export function insertionSort(array: number[]): number[] { + const arr = [...array]; + + for (let i = 1; i < arr.length; i++) { + const key = arr[i]; + let j = i - 1; + + while (j >= 0 && arr[j] > key) { + arr[j + 1] = arr[j]; + j--; + } + arr[j + 1] = key; + } + + return arr; +} + +/** + * Generate animation steps for Insertion Sort + */ +export function insertionSortSteps(array: number[]): AnimationStep[] { + const steps: AnimationStep[] = []; + const arr = [...array]; + + // Initial state + steps.push({ + id: `step-0`, + type: 'highlight', + indices: [], + description: 'Starting insertion sort', + codeLineNumbers: [1], + }); + + for (let i = 1; i < arr.length; i++) { + const key = arr[i]; + let j = i - 1; + + // Highlight current element + steps.push({ + id: `step-${steps.length}`, + type: 'highlight', + indices: [i], + description: `Select element ${key} at index ${i}`, + codeLineNumbers: [4, 5], + }); + + while (j >= 0 && arr[j] > key) { + // Compare + steps.push({ + id: `step-${steps.length}`, + type: 'compare', + indices: [j, j + 1], + description: `Compare ${arr[j]} with ${key}`, + codeLineNumbers: [7], + }); + + // Shift + arr[j + 1] = arr[j]; + steps.push({ + id: `step-${steps.length}`, + type: 'swap', + indices: [j, j + 1], + values: [...arr], + description: `Shift ${arr[j]} to the right`, + codeLineNumbers: [8], + }); + + j--; + } + + // Insert + arr[j + 1] = key; + steps.push({ + id: `step-${steps.length}`, + type: 'insert', + indices: [j + 1], + values: [...arr], + description: `Insert ${key} at index ${j + 1}`, + codeLineNumbers: [10], + }); + } + + // Mark all as sorted + steps.push({ + id: `step-${steps.length}`, + type: 'mark-sorted', + indices: arr.map((_, i) => i), + description: 'Array is sorted!', + codeLineNumbers: [12], + }); + + return steps; +} +``` + +### Step 2: Add Tests + +```typescript +// packages/algorithms/src/sorting/__tests__/insertion-sort.test.ts + +import { describe, it, expect } from 'vitest'; +import { insertionSort, insertionSortSteps } from '../insertion-sort'; + +describe('insertionSort', () => { + it('sorts an array correctly', () => { + expect(insertionSort([5, 3, 8, 1, 2])).toEqual([1, 2, 3, 5, 8]); + }); + + it('handles empty array', () => { + expect(insertionSort([])).toEqual([]); + }); + + it('handles single element', () => { + expect(insertionSort([1])).toEqual([1]); + }); + + it('handles already sorted array', () => { + expect(insertionSort([1, 2, 3, 4, 5])).toEqual([1, 2, 3, 4, 5]); + }); + + it('handles reverse sorted array', () => { + expect(insertionSort([5, 4, 3, 2, 1])).toEqual([1, 2, 3, 4, 5]); + }); + + it('handles duplicates', () => { + expect(insertionSort([3, 1, 3, 2, 1])).toEqual([1, 1, 2, 3, 3]); + }); +}); + +describe('insertionSortSteps', () => { + it('generates correct number of steps', () => { + const steps = insertionSortSteps([3, 1, 2]); + expect(steps.length).toBeGreaterThan(0); + }); + + it('first step is highlight', () => { + const steps = insertionSortSteps([3, 1, 2]); + expect(steps[0].type).toBe('highlight'); + }); + + it('last step marks all as sorted', () => { + const steps = insertionSortSteps([3, 1, 2]); + expect(steps[steps.length - 1].type).toBe('mark-sorted'); + }); +}); +``` + +### Step 3: Create the Visualizer Component + +```typescript +// packages/visualizers/src/sorting/InsertionSortVisualizer.tsx + +'use client'; + +import { useRef, useEffect, useMemo } from 'react'; +import { motion, AnimatePresence } from 'framer-motion'; +import type { VisualizerProps, AnimationStep } from '@opendsa/types'; +import { cn } from '@opendsa/utils'; + +interface InsertionSortProps extends VisualizerProps {} + +export function InsertionSortVisualizer({ + data, + currentStep, + steps, + isPlaying, + speed, +}: InsertionSortProps) { + const containerRef = useRef(null); + + const currentStepData = steps[currentStep]; + const maxValue = useMemo(() => Math.max(...data, 1), [data]); + + // Get the current state of the array at this step + const displayArray = useMemo(() => { + if (currentStepData?.values) { + return currentStepData.values as number[]; + } + return data; + }, [currentStepData, data]); + + const getBarColor = (index: number): string => { + if (!currentStepData) return 'bg-primary'; + + const { type, indices } = currentStepData; + + if (type === 'mark-sorted' && indices.includes(index)) { + return 'bg-green-500'; + } + if (type === 'compare' && indices.includes(index)) { + return 'bg-yellow-500'; + } + if (type === 'swap' && indices.includes(index)) { + return 'bg-red-500'; + } + if (type === 'highlight' && indices.includes(index)) { + return 'bg-blue-500'; + } + if (type === 'insert' && indices.includes(index)) { + return 'bg-purple-500'; + } + + return 'bg-primary'; + }; + + return ( +

+ + {displayArray.map((value, index) => ( + + {displayArray.length <= 20 && ( + + {value} + + )} + + ))} + +
+ ); +} +``` + +### Step 4: Create the Controls Component + +```typescript +// packages/visualizers/src/sorting/InsertionSortControls.tsx + +'use client'; + +import { useState } from 'react'; +import { + Button, + Slider, + Input, + Card, + CardContent, +} from '@opendsa/ui'; +import { + Play, + Pause, + RotateCcw, + SkipBack, + SkipForward, + Shuffle, +} from 'lucide-react'; +import type { ControlsProps } from '@opendsa/types'; + +export function InsertionSortControls({ + onPlay, + onPause, + onReset, + onStepForward, + onStepBackward, + onSpeedChange, + onDataChange, + isPlaying, + currentStep, + totalSteps, + speed, +}: ControlsProps) { + const [arraySize, setArraySize] = useState(10); + + const generateRandomArray = () => { + const newArray = Array.from( + { length: arraySize }, + () => Math.floor(Math.random() * 100) + 1 + ); + onDataChange(newArray); + }; + + return ( + + + {/* Playback Controls */} +
+ + + + + + + +
+ + {/* Progress */} +
+ Step {currentStep + 1} of {totalSteps} +
+ + {/* Speed Control */} +
+ + onSpeedChange(value)} + min={1} + max={100} + step={1} + /> +
+ + {/* Array Controls */} +
+ setArraySize(Number(e.target.value))} + min={2} + max={50} + className="w-20" + /> + +
+
+
+ ); +} +``` + +### Step 5: Register the Visualizer + +```typescript +// packages/visualizers/src/sorting/insertion-sort-plugin.ts + +import type { VisualizerPlugin } from '@opendsa/types'; +import { insertionSortSteps } from '@opendsa/algorithms'; +import { InsertionSortVisualizer } from './InsertionSortVisualizer'; +import { InsertionSortControls } from './InsertionSortControls'; + +export const insertionSortPlugin: VisualizerPlugin = { + meta: { + id: 'insertion-sort', + name: 'Insertion Sort', + category: 'sorting', + description: 'A simple sorting algorithm that builds the sorted array one element at a time.', + complexity: { + time: { + best: 'O(n)', + average: 'O(n²)', + worst: 'O(n²)', + }, + space: 'O(1)', + }, + tags: ['sorting', 'comparison', 'in-place', 'stable'], + difficulty: 'beginner', + }, + component: InsertionSortVisualizer, + controls: InsertionSortControls, + generateSteps: insertionSortSteps, + defaultInput: [64, 34, 25, 12, 22, 11, 90], + defaultCode: `function insertionSort(arr) { + for (let i = 1; i < arr.length; i++) { + let key = arr[i]; + let j = i - 1; + + while (j >= 0 && arr[j] > key) { + arr[j + 1] = arr[j]; + j--; + } + arr[j + 1] = key; + } + return arr; +}`, +}; + +// Register in packages/visualizers/src/registry.ts +import { registry } from './registry'; +import { insertionSortPlugin } from './sorting/insertion-sort-plugin'; + +registry.register(insertionSortPlugin); +``` + +### Step 6: Export from Package + +```typescript +// packages/visualizers/src/index.ts + +// ... existing exports +export { InsertionSortVisualizer } from './sorting/InsertionSortVisualizer'; +export { InsertionSortControls } from './sorting/InsertionSortControls'; +export { insertionSortPlugin } from './sorting/insertion-sort-plugin'; +``` + +### Step 7: Add Documentation + +Create a documentation page explaining the algorithm: + +```mdx +// apps/docs/pages/algorithms/sorting/insertion-sort.mdx + +--- +title: Insertion Sort +description: Learn how insertion sort works with interactive visualization +--- + +# Insertion Sort + +Insertion sort is a simple sorting algorithm that builds the final sorted array +one item at a time. + +## How It Works + +1. Start from the second element (index 1) +2. Compare it with elements before it +3. Shift larger elements to the right +4. Insert the element in its correct position +5. Repeat for all elements + +## Complexity + +| Case | Time | Space | +|------|------|-------| +| Best | O(n) | O(1) | +| Average | O(n²) | O(1) | +| Worst | O(n²) | O(1) | + +## When to Use + +- Small datasets +- Nearly sorted arrays +- Online sorting (sorting as data arrives) +- When simplicity is preferred over performance + +## Interactive Demo + +Visit the [Insertion Sort Visualizer](/visualize/sorting/insertion-sort) to see +the algorithm in action. +``` + + +## Code Style Guide + +### TypeScript + +- Use strict mode +- Prefer `interface` over `type` for object shapes +- Use descriptive variable names +- Add JSDoc comments for public APIs + +```typescript +// Good +interface UserPreferences { + theme: 'light' | 'dark' | 'system'; + fontSize: number; +} + +/** + * Updates user preferences in storage + * @param preferences - The preferences to save + * @returns Promise that resolves when saved + */ +async function savePreferences(preferences: UserPreferences): Promise { + // Implementation +} + +// Avoid +type prefs = { t: string; fs: number }; +const save = async (p: any) => { /* ... */ }; +``` + +### React Components + +- Use functional components with hooks +- Prefer named exports +- Use TypeScript for props +- Extract complex logic to custom hooks + +```typescript +// Good +interface ButtonProps { + variant?: 'primary' | 'secondary'; + children: React.ReactNode; + onClick?: () => void; +} + +export function Button({ variant = 'primary', children, onClick }: ButtonProps) { + return ( + + ); +} +``` + +### File Organization + +``` +ComponentName/ +├── ComponentName.tsx # Main component +├── ComponentName.test.tsx # Tests +├── use-component-name.ts # Custom hook (if needed) +├── types.ts # Types (if complex) +└── index.ts # Exports +``` + + +## Commit Guidelines + +We use [Conventional Commits](https://www.conventionalcommits.org/) for clear, automated changelogs. + +### Format + +``` +(): + +[optional body] + +[optional footer] +``` + +### Types + +| Type | Description | +|------|-------------| +| `feat` | New feature | +| `fix` | Bug fix | +| `docs` | Documentation changes | +| `style` | Code style changes (formatting) | +| `refactor` | Code refactoring | +| `perf` | Performance improvements | +| `test` | Adding or updating tests | +| `chore` | Maintenance tasks | +| `ci` | CI/CD changes | + +### Examples + +```bash +# Feature +feat(visualizers): add insertion sort visualizer + +# Bug fix +fix(animation): resolve timing issue in bubble sort + +# Documentation +docs(contributing): add visualizer creation guide + +# Multiple scopes +feat(algorithms,visualizers): add binary search +``` + +### Commit Message Guidelines + +- Use imperative mood ("add" not "added") +- First line max 72 characters +- Reference issues in footer: `Closes #123` + + +## Pull Request Process + +### Before Creating a PR + +1. **Sync with upstream** + ```bash + git fetch upstream + git rebase upstream/dev + ``` + +2. **Run all checks** + ```bash + pnpm lint + pnpm type-check + pnpm test + pnpm build + ``` + +3. **Update documentation** if needed + +### PR Template + +When you create a PR, fill out the template: + +```markdown +## Description +Brief description of changes + +## Type of Change +- [ ] Bug fix +- [ ] New feature +- [ ] Documentation +- [ ] Refactoring + +## Related Issues +Closes #123 + +## Checklist +- [ ] My code follows the code style +- [ ] I have added tests +- [ ] All tests pass +- [ ] I have updated documentation +- [ ] I have added a changeset (if applicable) + +## Screenshots (if applicable) +``` + +### Review Process + +1. **CI Checks**: All automated checks must pass +2. **Code Review**: At least 1 maintainer approval +3. **Discussion**: Address all comments +4. **Merge**: Maintainer merges when ready + +### After Merge + +- Delete your feature branch +- Pull latest changes +- Celebrate your contribution! + + +## Issue Guidelines + +### Bug Reports + +Use the bug report template: + +```markdown +**Describe the bug** +Clear description of the bug + +**To Reproduce** +1. Go to '...' +2. Click on '....' +3. See error + +**Expected behavior** +What should happen + +**Screenshots** +If applicable + +**Environment** +- OS: [e.g. Windows 11] +- Browser: [e.g. Chrome 120] +- Version: [e.g. 1.0.0] +``` + +### Feature Requests + +Use the feature request template: + +```markdown +**Is your feature request related to a problem?** +Clear description of the problem + +**Describe the solution you'd like** +What you want to happen + +**Describe alternatives you've considered** +Other solutions you've thought about + +**Additional context** +Any other information +``` + +### Visualizer Requests + +Use the new visualizer template: + +```markdown +**Algorithm/Data Structure** +Name of the algorithm + +**Category** +- [ ] Sorting +- [ ] Searching +- [ ] Graph +- [ ] Tree +- [ ] Dynamic Programming +- [ ] Data Structure + +**Description** +Brief description of the algorithm + +**Complexity** +- Time: O(?) +- Space: O(?) + +**Resources** +Links to explanations, Wikipedia, etc. + +**Would you like to implement this?** +- [ ] Yes, I'd like to work on this +- [ ] No, just suggesting +``` + + +## Community + +### Discord + +Join our Discord server for: +- Real-time help +- Feature discussions +- Showing off your contributions +- Connecting with other contributors + +[Join Discord⛓️‍💥❌](https://discord.gg/opendsa) + +### Getting Help + +- **Discord**: Quick questions, discussions +- **GitHub Issues**: Bugs, features, documentation +- **GitHub Discussions**: General questions, ideas + +### Recognition + +We appreciate all contributions! Contributors are: +- Listed in our README +- Mentioned in release notes +- Eligible for contributor badges + + +## License + +By contributing to OpenDSA, you agree that your contributions will be licensed under the MIT License. + + +*Thank you for contributing to OpenDSA! Your efforts help make algorithm learning more accessible to everyone.* diff --git a/apps/docs/app/globals.css b/apps/docs/app/globals.css new file mode 100644 index 0000000..e89ab1d --- /dev/null +++ b/apps/docs/app/globals.css @@ -0,0 +1,33 @@ +/* apps/docs/app/globals.css */ + +/* Base text: Inter for everything */ +body { + font-family: var(--font-inter), ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif; + font-size: 0.9rem; + font-weight: 400; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +/* Code blocks: JetBrains Mono */ +code, +kbd, +samp, +pre, +code[class*="language-"], +pre[class*="language-"] { + font-family: var(--font-jetbrains-mono), ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace !important; +} + +/* Nextra code block overrides */ +.nextra-code, +.nextra-code code, +[data-rehype-pretty-code-fragment] code, +[data-rehype-pretty-code-fragment] pre { + font-family: var(--font-jetbrains-mono), ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace !important; +} + +/* Inline code */ +:not(pre)>code { + font-family: var(--font-jetbrains-mono), ui-monospace, monospace !important; +} \ No newline at end of file diff --git a/apps/docs/app/layout.jsx b/apps/docs/app/layout.jsx index dfb8774..b30f140 100644 --- a/apps/docs/app/layout.jsx +++ b/apps/docs/app/layout.jsx @@ -2,13 +2,32 @@ import { Footer, Layout, Navbar } from 'nextra-theme-docs' import { Head } from 'nextra/components' import { getPageMap } from 'nextra/page-map' import 'nextra-theme-docs/style.css' +import './globals.css' +import 'katex/dist/katex.min.css' +import { JetBrains_Mono, Inter } from "next/font/google"; +// import Image from 'next/image' + +const jetbrainsMono = JetBrains_Mono({ + variable: "--font-jetbrains-mono", + subsets: ["latin"], + display: "swap", + weight: ["100", "200", "300", "400", "500", "600", "700"], +}); + +const inter = Inter({ + variable: "--font-inter", + subsets: ["latin"], + display: "swap", + weight: ["100", "200", "300", "400", "500", "600", "700"], +}); export const metadata = { title: { default: 'OpenDSA Docs', template: '%s | OpenDSA Docs' }, - description: 'OpenDSA documentation' + description: 'OpenDSA documentation', + authors: [{ name: "Solomon Eshun" }], } const navbar = ( @@ -17,18 +36,20 @@ const navbar = ( /> ) -const footer =
{new Date().getFullYear()} © OpenDSA.
+const footer =
+ {new Date().getFullYear()} © OpenDSA by {" "} Solo Shun. +
export default async function RootLayout({ children }) { return ( - + {children} diff --git a/apps/docs/app/migration/page.mdx b/apps/docs/app/migration/page.mdx deleted file mode 100644 index 24fc5c5..0000000 --- a/apps/docs/app/migration/page.mdx +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: Migration ---- - -This page will be migrated from `apps/docs/internal/OPENALGO_MIGRATION.md`. - diff --git a/apps/docs/app/page.mdx b/apps/docs/app/page.mdx index b0ec8dc..631ce11 100644 --- a/apps/docs/app/page.mdx +++ b/apps/docs/app/page.mdx @@ -1,8 +1,109 @@ --- -title: OpenDSA Docs +title: Introduction +description: Welcome to the OpenDSA documentation. --- -Welcome to the OpenDSA documentation. +# Introduction to OpenDSA -Use the sidebar to navigate. +> **See algorithms come to life** +OpenDSA is an interactive visualization platform that helps you understand how algorithms and data structures work through step-by-step animations. Whether you're: + +- **Learning** algorithms for the first time +- **Teaching** computer science concepts +- **Preparing** for technical interviews +- **Building** educational content + +OpenDSA makes complex algorithms visual and intuitive. + +## Features + +- **Interactive Visualizations** - Watch algorithms execute step-by-step with full control +- **Multiple Categories** - Sorting, searching, graphs, trees, and more +- **Code Highlighting** - See which line of code corresponds to each step +- **Speed Control** - Slow down or speed up animations +- **Shareable URLs** - Share specific visualization states with others +- **Dark/Light Mode** - Easy on the eyes, day or night +- **Keyboard Shortcuts** - Power-user friendly controls +- **Open Source** - Free forever, community-driven + +## Visualizers + +### Sorting Algorithms +- Bubble Sort +- Selection Sort +- Insertion Sort +- Quick Sort +- Merge Sort +- *More coming soon...* + +### Searching Algorithms +- Linear Search +- Binary Search +- *More coming soon...* + +### Data Structures +- Arrays +- *Linked Lists, Trees, Graphs coming soon...* + +## Quick Start + +### Try Online + +Visit [app.opendsa.dev](https://app.opendsa.dev.vercel.app) to start visualizing algorithms immediately - no installation required. + +### Run Locally + +```bash +# Clone the repository +git clone https://github.com/soloshun/opendsa.git +cd opendsa + +# Install dependencies +pnpm install + +# Start development server +pnpm dev +``` + +Open [http://localhost:3000](http://localhost:3000) in your browser. + +## Tech Stack + +| Layer | Technology | +|-------|------------| +| Framework | [Next.js 14](https://nextjs.org/) (App Router) | +| Language | [TypeScript](https://www.typescriptlang.org/) | +| Styling | [TailwindCSS](https://tailwindcss.com/) + [shadcn/ui](https://ui.shadcn.com/) | +| Animations | [Framer Motion](https://www.framer.com/motion/) + [D3.js](https://d3js.org/) | +| State | [Zustand](https://zustand-demo.pmnd.rs/) | +| Monorepo | [Turborepo](https://turbo.build/) | +| Package Manager | [pnpm](https://pnpm.io/) | +| Deployment | [Vercel](https://vercel.com/) | + +## Contributing + +We welcome contributions from everyone! Whether it's: + +- Adding new algorithm visualizers +- Fixing bugs +- Improving documentation +- Suggesting features + +See our [Contributing Guide](/contributing) to get started. + +## Community + +- **Discord**: [Join our community ⛓️‍💥❌](https://discord.gg/opendsa) +- **Twitter**: [@self.solo_shun⛓️‍💥❌](https://twitter.com/opendsa) +- **GitHub Discussions**: [Ask questions](https://github.com/soloshun/opendsa/discussions) + +
+
+

Made with ❤️ by the Solo Shun for the OpenDSA community

+ + \ No newline at end of file diff --git a/apps/docs/app/project/_meta.js b/apps/docs/app/project/_meta.js new file mode 100644 index 0000000..c0413b5 --- /dev/null +++ b/apps/docs/app/project/_meta.js @@ -0,0 +1,6 @@ +export default { + architecture: 'Architecture', + roadmap: 'Roadmap', + migration: 'Migration', + tech_stack: 'Tech Stack' +} diff --git a/apps/docs/app/project/architecture/page.mdx b/apps/docs/app/project/architecture/page.mdx new file mode 100644 index 0000000..35f4149 --- /dev/null +++ b/apps/docs/app/project/architecture/page.mdx @@ -0,0 +1,948 @@ +title: Architecture +description: Comprehensive architecture documentation for the OpenDSA algorithm visualization platform. + +# OpenDSA - System Architecture + +> Comprehensive architecture documentation for the OpenDSA algorithm visualization platform. + +## Table of Contents + +1. [Overview](#overview) +2. [High-Level Architecture](#high-level-architecture) +3. [Monorepo Structure](#monorepo-structure) +4. [Application Architecture](#application-architecture) +5. [Data Flow](#data-flow) +6. [Visualizer Plugin System](#visualizer-plugin-system) +7. [Animation Engine](#animation-engine) +8. [State Management](#state-management) +9. [Component Architecture](#component-architecture) +10. [API Design](#api-design) + + +## Overview + +OpenDSA is built as a **Turborepo monorepo** containing multiple applications and shared packages. This architecture enables: + +- **Code reuse** across applications (website, app, docs) +- **Independent deployments** for each application +- **Shared packages** for algorithms, UI components, and utilities +- **Scalable contributor experience** with clear boundaries + + +## High-Level Architecture + +```mermaid +flowchart TB; + subgraph Users [Users]; + Learner[Learner]; + Contributor[Contributor]; + Educator[Educator]; + end + + subgraph Apps [Applications]; + Web[Web - Landing Page]; + App[App - Visualizer]; + Docs[Docs - Documentation]; + end + + subgraph Packages [Shared Packages]; + UI[UI Components]; + Algorithms[Algorithms]; + Visualizers[Visualizers]; + Types[Types]; + Utils[Utils]; + Config[Config]; + end + + subgraph Infrastructure [Infrastructure]; + Vercel[Vercel Hosting]; + GitHub[GitHub Repository]; + Actions[GitHub Actions]; + end + + Users --> Apps; + Apps --> Packages; + Apps --> Infrastructure; + GitHub --> Actions; + Actions --> Vercel; +``` + +## Monorepo Structure + +```mermaid +flowchart LR; + subgraph Root[opendsa/]; + subgraph AppsDir[apps/]; + WebApp[web/]; + MainApp[app/]; + DocsApp[docs/]; + end + + subgraph PackagesDir[packages/]; + UIPackage[ui/]; + AlgoPackage[algorithms/]; + VizPackage[visualizers/]; + TypesPackage[types/]; + UtilsPackage[utils/]; + ConfigPackage[config/]; + end + + TurboConfig[turbo.json]; + RootPackage[package.json]; + Workspace[pnpm-workspace.yaml]; + end + + WebApp --> UIPackage; + WebApp --> ConfigPackage; + MainApp --> UIPackage; + MainApp --> AlgoPackage; + MainApp --> VizPackage; + MainApp --> TypesPackage; + MainApp --> UtilsPackage; + DocsApp --> UIPackage; + DocsApp --> ConfigPackage; +``` + +### Directory Details + +``` +opendsa/ +├── apps/ +│ ├── web/ # Marketing website +│ │ ├── app/ # Next.js App Router pages +│ │ │ ├── page.tsx # Landing page +│ │ │ ├── features/ # Features page +│ │ │ ├── community/ # Community page +│ │ │ ├── blog/ # Blog (MDX) +│ │ │ └── layout.tsx # Root layout +│ │ ├── components/ # Website-specific components +│ │ ├── public/ # Static assets +│ │ └── package.json +│ │ +│ ├── app/ # Main visualizer application +│ │ ├── app/ # Next.js App Router pages +│ │ │ ├── page.tsx # Dashboard +│ │ │ ├── visualize/ # Visualizer routes +│ │ │ │ └── [category]/ +│ │ │ │ └── [algorithm]/ +│ │ │ │ └── page.tsx +│ │ │ ├── playground/ # Playground mode +│ │ │ ├── learn/ # Learning paths +│ │ │ └── layout.tsx # App layout +│ │ ├── components/ # App-specific components +│ │ │ ├── visualizer/ # Visualizer wrappers +│ │ │ ├── controls/ # Control panels +│ │ │ ├── editor/ # Code editor +│ │ │ └── sidebar/ # Navigation sidebar +│ │ ├── lib/ # App utilities +│ │ │ ├── store.ts # Zustand store +│ │ │ └── hooks/ # Custom hooks +│ │ └── package.json +│ │ +│ └── docs/ # Documentation site +│ ├── app/ # Nextjs app +| │ ├── architecture/ +| | | └── page.mdx +| | └── etc... +│ ├── theme.config.tsx # Nextra config +│ ├── next.config.mjs # Next config +│ └── package.json +│ +├── packages/ +│ ├── ui/ # Shared UI components +│ │ ├── src/ +│ │ │ ├── components/ # shadcn/ui based components +│ │ │ │ ├── button.tsx +│ │ │ │ ├── card.tsx +│ │ │ │ ├── slider.tsx +│ │ │ │ └── ... +│ │ │ └── index.ts # Public exports +│ │ └── package.json +│ │ +│ ├── algorithms/ # Pure algorithm implementations +│ │ ├── src/ +│ │ │ ├── sorting/ +│ │ │ │ ├── bubble-sort.ts +│ │ │ │ ├── quick-sort.ts +│ │ │ │ ├── merge-sort.ts +│ │ │ │ ├── insertion-sort.ts +│ │ │ │ ├── selection-sort.ts +│ │ │ │ └── index.ts +│ │ │ ├── searching/ +│ │ │ │ ├── linear-search.ts +│ │ │ │ ├── binary-search.ts +│ │ │ │ ├── jump-search.ts +│ │ │ │ └── index.ts +│ │ │ ├── graph/ +│ │ │ │ ├── bfs.ts +│ │ │ │ ├── dfs.ts +│ │ │ │ ├── dijkstra.ts +│ │ │ │ └── index.ts +│ │ │ ├── tree/ +│ │ │ │ ├── bst.ts +│ │ │ │ ├── traversals.ts +│ │ │ │ └── index.ts +│ │ │ └── index.ts +│ │ ├── tests/ +│ │ └── package.json +│ │ +│ ├── visualizers/ # Visualization components +│ │ ├── src/ +│ │ │ ├── engine/ # Animation engine +│ │ │ │ ├── types.ts +│ │ │ │ ├── animation-engine.ts +│ │ │ │ ├── use-animation.ts +│ │ │ │ └── index.ts +│ │ │ ├── sorting/ +│ │ │ │ ├── BubbleSortVisualizer.tsx +│ │ │ │ ├── QuickSortVisualizer.tsx +│ │ │ │ └── index.ts +│ │ │ ├── searching/ +│ │ │ ├── graph/ +│ │ │ ├── tree/ +│ │ │ ├── registry.ts # Visualizer registry +│ │ │ └── index.ts +│ │ └── package.json +│ │ +│ ├── types/ # Shared TypeScript types +│ │ ├── src/ +│ │ │ ├── algorithm.types.ts +│ │ │ ├── visualizer.types.ts +│ │ │ ├── animation.types.ts +│ │ │ └── index.ts +│ │ └── package.json +│ │ +│ ├── utils/ # Shared utilities +│ │ ├── src/ +│ │ │ ├── array-utils.ts +│ │ │ ├── graph-utils.ts +│ │ │ ├── url-state.ts +│ │ │ └── index.ts +│ │ └── package.json +│ │ +│ └── config/ # Shared configurations +│ ├── eslint/ +│ ├── typescript/ +│ ├── tailwind/ +│ └── package.json +│ +├── .github/ +│ ├── workflows/ +│ │ ├── ci.yml +│ │ ├── release.yml +│ │ └── dependency-update.yml +│ ├── ISSUE_TEMPLATE/ +│ │ ├── bug_report.md +│ │ ├── feature_request.md +│ │ └── new_visualizer.md +│ ├── PULL_REQUEST_TEMPLATE.md +│ └── CODEOWNERS +│ +├── turbo.json +├── package.json +├── pnpm-lock.yaml +├── pnpm-workspace.yaml +├── CONTRIBUTING.md +├── CODE_OF_CONDUCT.md +├── LICENSE +└── README.md +``` + + +## Application Architecture + +### apps/app - Main Visualizer Application + +```mermaid +flowchart TB; + subgraph Pages[Pages - App Router]; + Dashboard[Dashboard Page]; + Visualize[Visualize Page]; + Playground[Playground Page]; + Learn[Learn Page]; + end + + subgraph Components[Components]; + Sidebar[Sidebar Navigation]; + VisualizerContainer[Visualizer Container]; + ControlPanel[Control Panel]; + CodeEditor[Code Editor]; + StepInfo[Step Information]; + end + + subgraph State[State Layer]; + ZustandStore[Zustand Store]; + URLState[URL State]; + LocalStorage[Local Storage]; + end + + subgraph Packages[External Packages]; + VisualizersPackage[visualizers]; + AlgorithmsPackage[algorithms]; + UIPackage2[ui]; + end + + Dashboard --> Sidebar; + Visualize --> VisualizerContainer; + Visualize --> ControlPanel; + Visualize --> CodeEditor; + Playground --> VisualizerContainer; + + VisualizerContainer --> VisualizersPackage; + ControlPanel --> UIPackage2; + CodeEditor --> Monaco[Monaco Editor]; + + Components --> State; + VisualizersPackage --> AlgorithmsPackage; +``` + +### Page Routing Structure + +```mermaid +flowchart LR; + subgraph Routes[URL Routes]; + Root["/"]; + VisualizeRoot["/visualize"]; + VisualizeCat["/visualize/sorting"]; + VisualizeAlgo["/visualize/sorting/bubble-sort"]; + PlaygroundRoute["/playground"]; + LearnRoute["/learn"]; + LearnPath["/learn/sorting-basics"]; + end + + Root --> Dashboard2[Dashboard]; + VisualizeRoot --> CategoryList[Category List]; + VisualizeCat --> AlgorithmList[Algorithm List]; + VisualizeAlgo --> VisualizerPage[Visualizer Page]; + PlaygroundRoute --> PlaygroundPage[Playground]; + LearnRoute --> LearningPaths[Learning Paths]; + LearnPath --> LessonPage[Lesson Page]; +``` + + +## Data Flow + +### Visualization Data Flow + +```mermaid +sequenceDiagram + participant User + participant UI as UI Controls + participant Store as Zustand Store + participant Engine as Animation Engine + participant Algo as Algorithm Package + participant Viz as Visualizer Component + + User->>UI: Set array [5, 3, 8, 1, 2] + UI->>Store: updateArray([5, 3, 8, 1, 2]) + User->>UI: Click "Play" + UI->>Store: play() + Store->>Algo: generateSteps(array) + Algo-->>Store: steps[] + Store->>Engine: startAnimation(steps) + + loop Each Step + Engine->>Store: setCurrentStep(n) + Store->>Viz: render(currentState) + Viz-->>User: Visual update + Engine->>Engine: wait(speed) + end + + Engine-->>Store: animationComplete() +``` + +### State Synchronization Flow + +```mermaid +flowchart LR; + subgraph Sources[State Sources]; + UserInput[User Input]; + URLParams[URL Parameters]; + Storage[Local Storage]; + end + + subgraph Store[Zustand Store]; + ArrayState[Array State]; + AnimationState[Animation State]; + PreferencesState[Preferences]; + end + + subgraph Sync[Synchronization]; + URLSync[URL Sync]; + StorageSync[Storage Sync]; + end + + UserInput --> Store; + URLParams --> URLSync; + URLSync --> Store; + Storage --> StorageSync; + StorageSync --> Store; + Store --> URLSync; + Store --> StorageSync; +``` + +## Visualizer Plugin System + +### Plugin Interface + +```typescript +// packages/types/src/visualizer.types.ts + +export type VisualizerCategory = + | 'sorting' + | 'searching' + | 'graph' + | 'tree' + | 'dp' + | 'data-structure'; + +export interface TimeComplexity { + best: string; + average: string; + worst: string; +} + +export interface Complexity { + time: TimeComplexity; + space: string; +} + +export interface VisualizerMeta { + id: string; + name: string; + category: VisualizerCategory; + description: string; + complexity: Complexity; + tags: string[]; + difficulty: 'beginner' | 'intermediate' | 'advanced'; +} + +export interface VisualizerProps { + data: T; + currentStep: number; + steps: AnimationStep[]; + isPlaying: boolean; + speed: number; +} + +export interface ControlsProps { + onPlay: () => void; + onPause: () => void; + onReset: () => void; + onStepForward: () => void; + onStepBackward: () => void; + onSpeedChange: (speed: number) => void; + onDataChange: (data: unknown) => void; + isPlaying: boolean; + currentStep: number; + totalSteps: number; + speed: number; +} + +export interface VisualizerPlugin { + meta: VisualizerMeta; + component: React.ComponentType>; + controls: React.ComponentType; + generateSteps: (input: TInput, options?: TOptions) => AnimationStep[]; + defaultInput: TInput; + defaultCode: string; + validateInput?: (input: unknown) => input is TInput; +} +``` + +### Plugin Registration + +```mermaid +flowchart TB; + subgraph Plugins[Visualizer Plugins]; + BubbleSort[BubbleSortPlugin]; + QuickSort[QuickSortPlugin]; + MergeSort[MergeSortPlugin]; + BinarySearch[BinarySearchPlugin]; + BFS[BFSPlugin]; + end + + subgraph Registry[Visualizer Registry]; + Register[register]; + Get[get]; + GetByCategory[getByCategory]; + GetAll[getAll]; + end + + subgraph App[Application]; + Router[Dynamic Router]; + Renderer[Visualizer Renderer]; + end + + Plugins --> Register; + Register --> Registry; + Router --> Get; + Get --> Renderer; +``` + +### Registry Implementation + +```typescript +// packages/visualizers/src/registry.ts + +import type { VisualizerPlugin, VisualizerCategory } from '@opendsa/types'; + +class VisualizerRegistry { + private plugins: Map = new Map(); + + register(plugin: VisualizerPlugin): void { + if (this.plugins.has(plugin.meta.id)) { + console.warn(`Plugin ${plugin.meta.id} already registered. Overwriting.`); + } + this.plugins.set(plugin.meta.id, plugin); + } + + get(id: string): VisualizerPlugin | undefined { + return this.plugins.get(id); + } + + getByCategory(category: VisualizerCategory): VisualizerPlugin[] { + return Array.from(this.plugins.values()) + .filter(plugin => plugin.meta.category === category); + } + + getAll(): VisualizerPlugin[] { + return Array.from(this.plugins.values()); + } + + getCategories(): VisualizerCategory[] { + const categories = new Set( + Array.from(this.plugins.values()).map(p => p.meta.category) + ); + return Array.from(categories); + } +} + +export const registry = new VisualizerRegistry(); +``` + + +## Animation Engine + +### Animation Types + +```typescript +// packages/types/src/animation.types.ts + +export type StepType = + | 'compare' + | 'swap' + | 'highlight' + | 'insert' + | 'delete' + | 'visit' + | 'mark-sorted' + | 'mark-found' + | 'pivot' + | 'partition'; + +export interface AnimationStep { + id: string; + type: StepType; + indices: number[]; + values?: unknown[]; + description: string; + codeLineNumbers?: number[]; + metadata?: Record; +} + +export interface AnimationState { + steps: AnimationStep[]; + currentStep: number; + isPlaying: boolean; + speed: number; // 1-100, percentage + direction: 'forward' | 'backward'; +} + +export interface AnimationControls { + play: () => void; + pause: () => void; + reset: () => void; + stepForward: () => void; + stepBackward: () => void; + goToStep: (step: number) => void; + setSpeed: (speed: number) => void; +} +``` + +### Animation Engine Architecture + +```mermaid +stateDiagram-v2 + [*] --> Idle + Idle --> Playing: play() + Playing --> Paused: pause() + Paused --> Playing: play() + Playing --> Idle: reset() + Paused --> Idle: reset() + Playing --> Completed: lastStep + Completed --> Idle: reset() + Completed --> Playing: play() from start + + Playing --> Playing: stepForward() + Paused --> Paused: stepForward() + Paused --> Paused: stepBackward() +``` + +### Animation Hook + +```typescript +// packages/visualizers/src/engine/use-animation.ts + +import { useCallback, useEffect, useRef } from 'react'; +import { useStore } from 'zustand'; +import type { AnimationStep, AnimationControls } from '@opendsa/types'; + +interface UseAnimationOptions { + steps: AnimationStep[]; + onStepChange?: (step: number, stepData: AnimationStep) => void; + onComplete?: () => void; +} + +export function useAnimation(options: UseAnimationOptions): AnimationControls { + const { steps, onStepChange, onComplete } = options; + const timerRef = useRef(null); + + // Implementation details... + + return { + play, + pause, + reset, + stepForward, + stepBackward, + goToStep, + setSpeed, + }; +} +``` + + +## State Management + +### Zustand Store Structure + +```mermaid +flowchart TB; + subgraph Store[Global Store]; + subgraph VisualizerSlice[Visualizer Slice]; + CurrentViz[currentVisualizer]; + Data[data]; + Steps[steps]; + CurrentStep[currentStep]; + end + + subgraph AnimationSlice[Animation Slice]; + IsPlaying[isPlaying]; + Speed[speed]; + Direction[direction]; + end + + subgraph PreferencesSlice[Preferences Slice]; + Theme[theme]; + EditorVisible[editorVisible]; + SidebarOpen[sidebarOpen]; + end + end + + subgraph Middleware[Middleware]; + Persist[persist]; + DevTools[devtools]; + URLSync2[url-sync]; + end + + Store --> Middleware; +``` + +### Store Implementation + +```typescript +// apps/app/lib/store.ts + +import { create } from 'zustand'; +import { persist, devtools } from 'zustand/middleware'; +import type { AnimationStep } from '@opendsa/types'; + +interface VisualizerState { + // Visualizer state + currentVisualizer: string | null; + data: unknown; + steps: AnimationStep[]; + currentStep: number; + + // Animation state + isPlaying: boolean; + speed: number; + + // Actions + setVisualizer: (id: string) => void; + setData: (data: unknown) => void; + setSteps: (steps: AnimationStep[]) => void; + setCurrentStep: (step: number) => void; + play: () => void; + pause: () => void; + reset: () => void; + setSpeed: (speed: number) => void; +} + +interface PreferencesState { + theme: 'light' | 'dark' | 'system'; + editorVisible: boolean; + sidebarOpen: boolean; + + setTheme: (theme: 'light' | 'dark' | 'system') => void; + toggleEditor: () => void; + toggleSidebar: () => void; +} + +export const useVisualizerStore = create()( + devtools( + (set, get) => ({ + currentVisualizer: null, + data: [], + steps: [], + currentStep: 0, + isPlaying: false, + speed: 50, + + setVisualizer: (id) => set({ currentVisualizer: id }), + setData: (data) => set({ data }), + setSteps: (steps) => set({ steps, currentStep: 0 }), + setCurrentStep: (step) => set({ currentStep: step }), + play: () => set({ isPlaying: true }), + pause: () => set({ isPlaying: false }), + reset: () => set({ currentStep: 0, isPlaying: false }), + setSpeed: (speed) => set({ speed }), + }), + { name: 'visualizer-store' } + ) +); + +export const usePreferencesStore = create()( + persist( + (set) => ({ + theme: 'system', + editorVisible: true, + sidebarOpen: true, + + setTheme: (theme) => set({ theme }), + toggleEditor: () => set((state) => ({ editorVisible: !state.editorVisible })), + toggleSidebar: () => set((state) => ({ sidebarOpen: !state.sidebarOpen })), + }), + { name: 'opendsa-preferences' } + ) +); +``` + + +## Component Architecture + +### Component Hierarchy + +```mermaid +flowchart TB; + subgraph AppLayout[App Layout]; + Header[Header]; + Sidebar2[Sidebar]; + MainContent[Main Content]; + end + + subgraph VisualizerPage[Visualizer Page]; + VisualizerHeader[Visualizer Header]; + VisualizerBody[Visualizer Body]; + VisualizerFooter[Visualizer Footer]; + end + + subgraph VisualizerBodyContent[Visualizer Body Content]; + VisualizerPanel[Visualizer Panel]; + EditorPanel[Editor Panel]; + end + + subgraph VisualizerPanelContent[Visualizer Panel]; + Canvas[Canvas/SVG]; + StepIndicator[Step Indicator]; + ControlBar[Control Bar]; + end + + subgraph EditorPanelContent[Editor Panel]; + CodeEditor2[Code Editor]; + Console[Console Output]; + end + + MainContent --> VisualizerPage; + VisualizerBody --> VisualizerBodyContent; + VisualizerBodyContent --> VisualizerPanelContent; + VisualizerBodyContent --> EditorPanelContent; +``` + +### Component Responsibilities + +| Component | Responsibility | +|-----------|---------------| +| `AppLayout` | Global layout, navigation, theme | +| `Sidebar` | Algorithm/category navigation | +| `VisualizerPage` | Page-level state, data fetching | +| `VisualizerPanel` | Render visualization, handle resize | +| `ControlBar` | Play/pause, speed, step controls | +| `EditorPanel` | Code display, syntax highlighting | +| `StepIndicator` | Current step info, progress | + + +## API Design + +### Internal Package APIs + +```typescript +// @opendsa/algorithms - Algorithm package exports +export { bubbleSort, bubbleSortSteps } from './sorting/bubble-sort'; +export { quickSort, quickSortSteps } from './sorting/quick-sort'; +export { mergeSort, mergeSortSteps } from './sorting/merge-sort'; +export { linearSearch, linearSearchSteps } from './searching/linear-search'; +export { binarySearch, binarySearchSteps } from './searching/binary-search'; +export { bfs, bfsSteps } from './graph/bfs'; +export { dfs, dfsSteps } from './graph/dfs'; + +// @opendsa/visualizers - Visualizer package exports +export { registry } from './registry'; +export { useAnimation } from './engine/use-animation'; +export { BubbleSortVisualizer } from './sorting/BubbleSortVisualizer'; +export { QuickSortVisualizer } from './sorting/QuickSortVisualizer'; +// ... other visualizers + +// @opendsa/ui - UI component exports +export { Button } from './components/button'; +export { Card } from './components/card'; +export { Slider } from './components/slider'; +export { Input } from './components/input'; +// ... other components + +// @opendsa/types - Type exports +export type { AnimationStep, StepType } from './animation.types'; +export type { VisualizerPlugin, VisualizerMeta } from './visualizer.types'; +export type { AlgorithmResult } from './algorithm.types'; +``` + +### URL State API + +```typescript +// URL structure for sharing visualizations +// https://app.opendsa.dev/visualize/sorting/bubble-sort?data=5,3,8,1,2&step=5&speed=50 + +interface URLState { + data?: string; // Comma-separated values + step?: number; // Current step + speed?: number; // Animation speed (1-100) + playing?: boolean; // Auto-play on load +} + +// Utility functions +export function encodeURLState(state: Partial): string; +export function decodeURLState(search: string): Partial; +export function useURLState(): [URLState, (state: Partial) => void]; +``` + + +## Security Considerations + +### Code Execution + +- User code runs in a **sandboxed Web Worker** +- No access to DOM, localStorage, or network +- Execution timeout limits (5 seconds default) +- Memory limits enforced + +### Input Validation + +- All user inputs validated before processing +- Array size limits (max 100 elements for visualization) +- Value range limits +- Sanitization of any user-provided strings + + +## Performance Optimizations + +### Rendering + +- **Canvas API** for large array visualizations (>50 elements) +- **SVG** for smaller, more detailed visualizations +- **React.memo** for static components +- **useMemo/useCallback** for expensive computations + +### Animation + +- **RequestAnimationFrame** for smooth animations +- **Web Workers** for step generation (large inputs) +- **Virtualization** for step history (>1000 steps) + +### Bundle Size + +- **Tree shaking** enabled for all packages +- **Dynamic imports** for visualizers (code split per visualizer) +- **Lazy loading** for Monaco Editor + + +## Accessibility + +### Requirements + +- **WCAG 2.1 AA** compliance target +- **Keyboard navigation** for all controls +- **Screen reader** announcements for step changes +- **High contrast** mode support +- **Reduced motion** preference respected + +### Implementation + +```typescript +// Accessibility hook for visualizations +export function useA11yAnnouncements() { + const announce = useCallback((message: string) => { + // Use aria-live region to announce changes + }, []); + + return { announce }; +} +``` + + +## Testing Strategy + +### Unit Tests (Vitest) + +- Algorithm correctness +- Step generation accuracy +- Utility functions +- Store actions/selectors + +### Component Tests (Testing Library) + +- Component rendering +- User interactions +- State updates +- Accessibility + +### E2E Tests (Playwright) + +- Full user flows +- Cross-browser testing +- Visual regression +- Performance benchmarks + + +## Conclusion + +This architecture provides: + +1. **Scalability** - Easy to add new algorithms and visualizers +2. **Maintainability** - Clear separation of concerns +3. **Contributor-friendly** - Well-defined plugin system +4. **Performance** - Optimized rendering and animations +5. **Accessibility** - Built-in from the start +6. **Type safety** - Full TypeScript coverage + +The modular design ensures that OpenDSA can grow with its community while maintaining code quality and user experience. diff --git a/apps/docs/app/project/migration/page.mdx b/apps/docs/app/project/migration/page.mdx new file mode 100644 index 0000000..f5634a6 --- /dev/null +++ b/apps/docs/app/project/migration/page.mdx @@ -0,0 +1,42 @@ +--- +title: Migration +description: Overview of the migration from the legacy project to OpenDSA. +--- + +# Migration to OpenDSA + +> A high-level overview of migrating from the legacy [ds-algo-deck](https://dsalgodeck.netlify.app/) project to the new OpenDSA platform. + +## Overview + +The OpenDSA project is a complete rewrite and modernization of the previous [`ds-algo-deck`](https://dsalgodeck.netlify.app/) visualization tool. This migration was driven by the need for better scalability, performance, and a more robust contributor experience. + +## Why Migrate? + +The legacy project was built with **React + Vite** and **Redux Toolkit**. While functional, as the project grew, we encountered limitations in: + +- **Scalability**: Managing a growing number of visualizers in a single repository without clear boundaries was becoming difficult. +- **Performance**: Heavy visualizations started to impact UI responsiveness. +- **Maintainability**: Mixed styling approaches (Tailwind + Styled Components) and lack of strict typing made contributions harder. + +## The New Stack + +OpenDSA introduces a modern, monorepo-based architecture: + +- **Next.js & Turborepo**: For efficient build pipelines and server-side capabilities. +- **TypeScript**: Strict type safety across the entire codebase. +- **Zustand**: Lightweight, performant state management. +- **shadcn/ui**: a cohesive, accessible design system. + +| Aspect | Legacy (ds-algo-deck) | OpenDSA | +|--------|------------------------|---------| +| **Core** | React SPA | Next.js App Router | +| **Repo** | Single Repo | Monorepo (Turborepo) | +| **State** | Redux | Zustand | +| **Styles** | Mixed | TailwindCSS + shadcn/ui | + +## Legacy Repository + +This documentation serves as the new home for the project. The old repository is kept for reference but development has moved here. + +For detailed internal migration steps used during the transition, please refer to the internal documentation or the project's commit history. diff --git a/apps/docs/app/project/roadmap/page.mdx b/apps/docs/app/project/roadmap/page.mdx new file mode 100644 index 0000000..2b5c5f3 --- /dev/null +++ b/apps/docs/app/project/roadmap/page.mdx @@ -0,0 +1,581 @@ +--- +title: Roadmap +description: A phased approach to building the ultimate algorithm visualization platform. +--- + +# OpenDSA - Product Roadmap + +> A phased approach to building the ultimate algorithm visualization platform. + +## Table of Contents + +1. [Vision](#vision) +2. [Roadmap Overview](#roadmap-overview) +3. [Phase 1: Foundation](#phase-1-foundation-mvp) +4. [Phase 2: Core Features](#phase-2-core-features) +5. [Phase 3: Community and Learning](#phase-3-community-and-learning) +6. [Phase 4: Advanced Features](#phase-4-advanced-features) +7. [Future Considerations](#future-considerations) +8. [Success Metrics](#success-metrics) + + +## Vision + +**OpenDSA** aims to be the go-to open-source platform for visualizing and learning data structures and algorithms. Our goal is to make algorithm education: + +- **Visual**: See algorithms come to life +- **Interactive**: Control and experiment with algorithms +- **Accessible**: Free and open to everyone +- **Community-driven**: Built by learners, for learners + + +## Roadmap Overview + +```mermaid +gantt + title OpenDSA Development Roadmap + dateFormat YYYY-MM + + section Phase 1 + Foundation/MVP :p1, 2026-01-24, 3M + + section Phase 2 + Core Features :p2, after p1, 3M + + section Phase 3 + Community/Learning :p3, after p2, 3M + + section Phase 4 + Advanced Features :p4, after p3, 3M +``` + +### Phase Summary + +| Phase | Focus | Duration | Status | +|-------|-------|----------|--------| +| Phase 1 | Foundation & MVP | ~3 months | Planned | +| Phase 2 | Core Features | ~3 months | Planned | +| Phase 3 | Community & Learning | ~3 months | Planned | +| Phase 4 | Advanced Features | ~3 months | Planned | + + +## Phase 1: Foundation (MVP) + +**Goal**: Establish the technical foundation and launch with essential features. + +### 1.1 Infrastructure Setup + +- [X] **Repository Setup** + - Initialize Turborepo monorepo + - Configure pnpm workspaces + - Set up TypeScript strict mode + - Configure ESLint + Prettier + - Set up Husky for git hooks + +- [ ] **CI/CD Pipeline** + - GitHub Actions for CI (lint, type-check, test, build) + - Vercel deployment configuration + - Preview deployments for PRs + - Automated dependency updates (Dependabot) + +- [ ] **Core Packages** + - `@opendsa/types` - Shared TypeScript types + - `@opendsa/config` - Shared configurations + - `@opendsa/utils` - Utility functions + +### 1.2 UI Component Library + +- [ ] **Base Components** (`@opendsa/ui`) + - Button, Input, Slider + - Card, Dialog, Popover + - Dropdown, Select, Tabs + - Toast notifications + - Theme provider (light/dark) + +- [ ] **Visualizer Components** + - Bar chart component (for sorting) + - Array visualization component + - Control panel component + - Step indicator component + - Speed slider component + +### 1.3 Animation Engine + +- [ ] **Core Engine** (`@opendsa/visualizers`) + - Step-based animation system + - Play/pause/reset controls + - Step forward/backward + - Speed control (1x - 10x) + - Animation state management + +- [ ] **Animation Hooks** + - `useAnimation` hook + - `useVisualizerStore` hook + - Keyboard shortcuts support + +### 1.4 Initial Algorithms + +- [ ] **Sorting Algorithms** (`@opendsa/algorithms`) + - Bubble Sort + - Selection Sort + - Insertion Sort + - Quick Sort + - Merge Sort + +- [ ] **Searching Algorithms** + - Linear Search + - Binary Search + +- [ ] **Data Structure Operations** + - Array: insert, delete, update, search + +### 1.5 Visualizer Application + +- [ ] **App Structure** (`apps/app`) + - Next.js App Router setup + - Layout with sidebar navigation + - Dynamic visualizer routes + - Responsive design (tablet+) + +- [ ] **Visualizer Pages** + - Sorting algorithm visualizers (5) + - Searching algorithm visualizers (2) + - Array operations visualizer + +- [ ] **Features** + - Play/pause animations + - Step-by-step mode + - Speed control + - Random array generation + - Custom array input + +### 1.6 Marketing Website + +- [X] **Landing Page** (`apps/web`) + - Hero section with demo + - Features overview + - Algorithm categories + - Call to action + +- [X] **Pages** + - Home (landing) + - Features + - About + +### 1.7 Documentation Site + +- [ ] **Initial Docs** (`apps/docs`) + - Getting started guide + - Installation instructions + - Quick start tutorial + - Contributing guide + +### 1.8 Launch Checklist + +- [ ] Domain setup (opendsa.dev) +- [ ] SEO configuration +- [ ] Analytics setup +- [ ] Error tracking (Sentry) +- [ ] README and LICENSE +- [ ] Social media presence +- [ ] Launch announcement + + +## Phase 2: Core Features + +**Goal**: Expand algorithm coverage and enhance the visualization experience. + +### 2.1 Additional Sorting Algorithms + +- [ ] Heap Sort +- [ ] Counting Sort +- [ ] Radix Sort +- [ ] Bucket Sort +- [ ] Shell Sort +- [ ] Tim Sort (simplified) + +### 2.2 Additional Searching Algorithms + +- [ ] Jump Search +- [ ] Interpolation Search +- [ ] Exponential Search +- [ ] Ternary Search + +### 2.3 Graph Algorithms + +- [ ] **Graph Data Structure** + - Graph visualization component + - Node/edge creation UI + - Adjacency list/matrix views + +- [ ] **Traversal Algorithms** + - Breadth-First Search (BFS) + - Depth-First Search (DFS) + +- [ ] **Shortest Path** + - Dijkstra's Algorithm + - Bellman-Ford Algorithm + - Floyd-Warshall Algorithm + +- [ ] **Minimum Spanning Tree** + - Prim's Algorithm + - Kruskal's Algorithm + +### 2.4 Tree Algorithms + +- [ ] **Binary Search Tree** + - Insert, Delete, Search + - Tree visualization component + - Balance visualization + +- [ ] **Tree Traversals** + - Inorder + - Preorder + - Postorder + - Level order + +- [ ] **Advanced Trees** (simplified) + - AVL Tree rotations + - Red-Black Tree concepts + +### 2.5 Data Structure Visualizers + +- [ ] **Linked List** + - Singly linked list + - Doubly linked list + - Insert, delete, reverse + +- [ ] **Stack** + - Push, pop, peek + - Expression evaluation demo + +- [ ] **Queue** + - Enqueue, dequeue + - Circular queue + - Priority queue concepts + +- [ ] **Hash Table** + - Insert, search, delete + - Collision handling visualization + +### 2.6 Enhanced Features + +- [ ] **Code Editor Integration** + - Monaco Editor setup + - Syntax highlighting + - Code step highlighting + - Multiple language support (JS, Python, Java, C++) + +- [ ] **Shareable URLs** + - Encode state in URL + - Share specific visualization state + - Deep linking to steps + +- [ ] **User Preferences** + - Theme persistence + - Speed preferences + - Layout preferences + - Keyboard shortcuts customization + +### 2.7 Performance Optimizations + +- [ ] Canvas rendering for large arrays +- [ ] Virtualization for step history +- [ ] Web Worker for step generation +- [ ] Bundle size optimization + + +## Phase 3: Community and Learning + +**Goal**: Build educational content and community engagement features. + +### 3.1 Learning Paths + +- [ ] **Structured Courses** + - "Introduction to Sorting" + - "Mastering Searching" + - "Graph Algorithms 101" + - "Trees and Recursion" + +- [ ] **Path Features** + - Progress tracking + - Prerequisites + - Estimated duration + - Difficulty levels + +### 3.2 Interactive Tutorials + +- [ ] **Guided Walkthroughs** + - Step-by-step explanations + - Interactive quizzes + - "Try it yourself" sections + - Hints and solutions + +- [ ] **Concept Explanations** + - Time complexity visualizer + - Space complexity explanations + - Big O comparison charts + - Real-world applications + +### 3.3 Challenge Mode + +- [ ] **Algorithm Challenges** + - Predict the output + - Count the steps + - Identify the algorithm + - Fix the bug + +- [ ] **Gamification** + - Points/scoring system + - Streaks and achievements + - Leaderboard (optional) + - Progress badges + +### 3.4 Community Features + +- [ ] **Discord Integration** + - Discord server setup + - Role assignments + - Channel structure + - Bot for announcements + +- [ ] **Community Submissions** + - User-submitted visualizers + - Review process + - Attribution system + +- [ ] **Feedback System** + - In-app feedback + - Feature voting + - Bug reporting + +### 3.5 Blog + +- [ ] **Algorithm Deep Dives** + - Detailed explanations + - Use case examples + - Implementation tips + +- [ ] **Community Spotlights** + - Contributor features + - Project updates + - Release notes + +### 3.6 Documentation Expansion + +- [ ] **Algorithm Reference** + - Every algorithm documented + - Complexity analysis + - Code examples + - Related problems + +- [ ] **API Documentation** + - Package APIs + - Hook documentation + - Type definitions + + +## Phase 4: Advanced Features + +**Goal**: Add power-user features and platform expansion capabilities. + +### 4.1 User Accounts (Optional) + +- [ ] **Authentication** + - GitHub OAuth + - Google OAuth + - Email/password (optional) + +- [ ] **User Features** + - Save favorite visualizations + - Track learning progress + - Custom settings sync + - Submission history + +### 4.2 Save and Load + +- [ ] **Visualization Saving** + - Save current state + - Name and organize + - Export/import JSON + +- [ ] **Collections** + - Create collections + - Share collections + - Public/private visibility + +### 4.3 Embed Widget + +- [ ] **Embeddable Component** + - iframe embed code + - Web component version + - Customization options + +- [ ] **Use Cases** + - Blog posts + - Documentation + - Educational platforms + - Coding bootcamps + +### 4.4 API + +- [ ] **Public API** + - Algorithm execution + - Step generation + - Embed configuration + +- [ ] **Integration Support** + - npm package for algorithms + - React component library + - Documentation + +### 4.5 Advanced Algorithms + +- [ ] **Dynamic Programming** + - Fibonacci visualization + - Longest Common Subsequence + - 0/1 Knapsack + - Matrix Chain Multiplication + +- [ ] **String Algorithms** + - Pattern matching (KMP) + - Rabin-Karp + - Trie operations + +- [ ] **Advanced Graph** + - Topological Sort + - Strongly Connected Components + - Network Flow concepts + +### 4.6 Mobile Experience + +- [ ] **Responsive Improvements** + - Mobile-optimized visualizations + - Touch gestures + - Portrait mode support + +- [ ] **PWA Features** + - Offline support + - Install prompt + - Push notifications (updates) + +### 4.7 Accessibility Audit + +- [ ] **WCAG 2.1 AA Compliance** + - Screen reader support + - Keyboard navigation + - High contrast mode + - Reduced motion support + +- [ ] **Documentation** + - Accessibility statement + - Known issues + - Alternative formats + +### 4.8 Internationalization + +- [ ] **i18n Setup** + - Translation framework + - Language selector + - RTL support + +- [ ] **Initial Languages** + - English (default) + - Spanish + - Chinese + - Hindi + - (Community contributions) + + +## Future Considerations + +These are ideas for future exploration, not committed features: + +### Potential Features + +- **AI-Powered Explanations**: Use LLMs to generate custom explanations +- **Collaborative Mode**: Real-time shared visualization sessions +- **VR/AR Visualization**: 3D algorithm visualization +- **Interview Prep Mode**: LeetCode-style practice integration +- **Curriculum Builder**: Tools for educators to create courses +- **Native Mobile Apps**: iOS/Android applications +- **Desktop App**: Electron-based desktop application + +### Integration Ideas + +- **LeetCode/HackerRank**: Link to practice problems +- **GitHub**: Import/export algorithm implementations +- **Notion/Obsidian**: Embed widgets +- **VS Code Extension**: In-editor visualization + + +## Success Metrics + +### Phase 1 Metrics + +| Metric | Target | +|--------|--------| +| GitHub Stars | 100+ | +| Monthly Active Users | 500+ | +| Visualizers Available | 10+ | +| Documentation Pages | 20+ | +| Contributors | 5+ | + +### Phase 2 Metrics + +| Metric | Target | +|--------|--------| +| GitHub Stars | 500+ | +| Monthly Active Users | 2,000+ | +| Visualizers Available | 30+ | +| Documentation Pages | 50+ | +| Contributors | 15+ | + +### Phase 3 Metrics + +| Metric | Target | +|--------|--------| +| GitHub Stars | 2,000+ | +| Monthly Active Users | 10,000+ | +| Learning Paths | 5+ | +| Discord Members | 500+ | +| Blog Posts | 20+ | + +### Phase 4 Metrics + +| Metric | Target | +|--------|--------| +| GitHub Stars | 5,000+ | +| Monthly Active Users | 50,000+ | +| Embed Integrations | 100+ | +| API Calls/Month | 10,000+ | +| Languages Supported | 5+ | + + +## How to Contribute to the Roadmap + +We welcome community input on our roadmap: + +1. **Feature Requests**: Open an issue with the "feature request" template +2. **Voting**: React to issues to show support for features +3. **Discussion**: Join our Discord to discuss roadmap items +4. **Implementation**: Volunteer to implement roadmap items + +### Prioritization Criteria + +Features are prioritized based on: + +1. **Impact**: How many users will benefit +2. **Effort**: Development complexity +3. **Dependencies**: What needs to be built first +4. **Community Interest**: Votes and feedback +5. **Strategic Fit**: Alignment with project vision + + +## Changelog + +| Date | Change | +|------|--------| +| 2026-01-24 | Initial roadmap created | + +*This roadmap is a living document and will be updated as the project evolves.* diff --git a/apps/docs/app/project/tech_stack/page.mdx b/apps/docs/app/project/tech_stack/page.mdx new file mode 100644 index 0000000..0fbc9ef --- /dev/null +++ b/apps/docs/app/project/tech_stack/page.mdx @@ -0,0 +1,1049 @@ +--- +title: Tech Stack +description: Comprehensive documentation of all technology choices and the rationale behind each decision. +--- + +# OpenDSA - Technology Stack + +> Comprehensive documentation of all technology choices and the rationale behind each decision. + +## Table of Contents + +1. [Overview](#overview) +2. [Core Framework](#core-framework) +3. [Build System](#build-system) +4. [UI and Styling](#ui-and-styling) +5. [Animations and Visualizations](#animations-and-visualizations) +6. [State Management](#state-management) +7. [Code Editor](#code-editor) +8. [Testing](#testing) +9. [Documentation](#documentation) +10. [Deployment](#deployment) +11. [Development Tools](#development-tools) +12. [Comparison with Current Stack](#comparison-with-current-stack) + + +## Overview + +The OpenDSA tech stack is chosen based on the following criteria: + +- **Industry Standard**: Widely adopted, well-documented technologies +- **Developer Experience**: Fast iteration, good tooling, TypeScript support +- **Performance**: Optimized for smooth animations and responsive UI +- **Open Source Friendly**: Easy for contributors to understand and work with +- **Scalability**: Can grow with the project's needs +- **Maintainability**: Long-term support, active communities + +```mermaid +flowchart TB; + subgraph Frontend[Frontend Layer]; + NextJS[Next.js 14+]; + React[React 18+]; + TypeScript[TypeScript 5+]; + end + + subgraph Build[Build Layer]; + Turborepo[Turborepo]; + PNPM[pnpm]; + end + + subgraph UI[UI Layer]; + ShadcnUI[shadcn/ui]; + TailwindCSS[TailwindCSS]; + RadixUI[Radix UI]; + end + + subgraph Animation[Animation Layer]; + FramerMotion[Framer Motion]; + D3JS[D3.js]; + CanvasAPI[Canvas API]; + end + + subgraph State[State Layer]; + Zustand[Zustand]; + TanStackQuery[TanStack Query]; + end + + subgraph Testing2[Testing Layer]; + Vitest[Vitest]; + Playwright[Playwright]; + TestingLibrary[Testing Library]; + end + + subgraph Deploy[Deployment]; + Vercel[Vercel]; + GitHubActions[GitHub Actions]; + end + + Frontend --> Build; + Frontend --> UI; + Frontend --> Animation; + Frontend --> State; + Frontend --> Testing2; + Build --> Deploy; +``` + + +## Core Framework + +### Next.js 14+ (App Router) + +**Choice**: Next.js with App Router + +**Alternatives Considered**: +- Vite + React (current stack) +- Remix +- Astro +- Create React App + +**Rationale**: + +| Feature | Next.js | Vite + React | Remix | +|---------|---------|--------------|-------| +| SSR/SSG | Built-in | Manual | Built-in | +| File-based routing | Yes | No | Yes | +| API routes | Yes | No | Yes | +| Image optimization | Built-in | Manual | Manual | +| Vercel integration | Native | Good | Good | +| App Router (RSC) | Yes | No | No | +| Community/Ecosystem | Largest | Large | Growing | + +**Key Benefits**: +1. **Server Components**: Reduce client bundle size +2. **File-based Routing**: Intuitive, less boilerplate +3. **Built-in Optimizations**: Images, fonts, scripts +4. **Vercel Deployment**: Zero-config, preview deployments +5. **SEO**: Better for marketing site +6. **Ecosystem**: Vast plugin and tool support + +**Configuration**: +```typescript +// next.config.js +/** @type {import('next').NextConfig} */ +const nextConfig = { + experimental: { + typedRoutes: true, + }, + images: { + remotePatterns: [ + { hostname: 'avatars.githubusercontent.com' }, + ], + }, + transpilePackages: [ + '@opendsa/ui', + '@opendsa/visualizers', + '@opendsa/algorithms', + ], +}; + +export default nextConfig; +``` + + +### TypeScript 5+ + +**Choice**: TypeScript with strict mode + +**Alternatives Considered**: +- JavaScript (current stack) +- JSDoc type annotations + +**Rationale**: + +1. **Type Safety**: Catch errors at compile time +2. **Better DX**: Autocomplete, refactoring support +3. **Documentation**: Types serve as documentation +4. **Contributor Experience**: Easier to understand codebase +5. **Industry Standard**: Expected for serious projects + +**Configuration**: +```json +// tsconfig.json +{ + "compilerOptions": { + "strict": true, + "noUncheckedIndexedAccess": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "exactOptionalPropertyTypes": true, + "moduleResolution": "bundler", + "module": "ESNext", + "target": "ES2022", + "lib": ["DOM", "DOM.Iterable", "ES2022"], + "jsx": "preserve", + "incremental": true, + "paths": { + "@/*": ["./src/*"], + "@opendsa/ui": ["../../packages/ui/src"], + "@opendsa/algorithms": ["../../packages/algorithms/src"], + "@opendsa/visualizers": ["../../packages/visualizers/src"], + "@opendsa/types": ["../../packages/types/src"], + "@opendsa/utils": ["../../packages/utils/src"] + } + } +} +``` + + +### pnpm + +**Choice**: pnpm as package manager + +**Alternatives Considered**: +- npm (current) +- yarn +- bun + +**Rationale**: + +| Feature | pnpm | npm | yarn | bun | +|---------|------|-----|------|-----| +| Disk space | Best | Worst | Medium | Best | +| Install speed | Fast | Slow | Medium | Fastest | +| Monorepo support | Native | Limited | Good | Growing | +| Strict mode | Yes | No | No | No | +| Stability | Excellent | Excellent | Good | Beta | + +**Key Benefits**: +1. **Efficient Storage**: Content-addressable store, symlinks +2. **Fast**: Up to 2x faster than npm +3. **Strict**: Prevents phantom dependencies +4. **Monorepo Native**: Built-in workspace support +5. **Industry Adoption**: Used by Vue, Vite, Turborepo + + +## Build System + +### Turborepo + +**Choice**: Turborepo for monorepo management + +**Alternatives Considered**: +- Nx +- Lerna +- Rush +- No monorepo (separate repos) + +**Rationale**: + +| Feature | Turborepo | Nx | Lerna | +|---------|-----------|-----|-------| +| Learning curve | Low | High | Medium | +| Build caching | Excellent | Excellent | Limited | +| Remote caching | Vercel | Nx Cloud | No | +| Configuration | Minimal | Complex | Moderate | +| Vercel integration | Native | Manual | Manual | + +**Key Benefits**: +1. **Simple Configuration**: Single `turbo.json` +2. **Intelligent Caching**: Local and remote caching +3. **Parallel Execution**: Run tasks in parallel +4. **Vercel Integration**: Free remote caching +5. **Incremental Builds**: Only rebuild what changed + +**Configuration** (Turborepo 2.0+): +```json +// turbo.json +{ + "$schema": "https://turborepo.dev/schema.json", + "globalDependencies": ["**/.env.*local"], + "tasks": { + "build": { + "dependsOn": ["^build"], + "outputs": [".next/**", "!.next/cache/**", "dist/**"] + }, + "lint": {}, + "test": { + "dependsOn": ["^build"] + }, + "dev": { + "cache": false, + "persistent": true + }, + "type-check": { + "dependsOn": ["^type-check"] + } + } +} +``` + + +## UI and Styling + +### shadcn/ui + +**Choice**: shadcn/ui component library + +**Alternatives Considered**: +- NextUI (current) +- Radix UI (raw) +- Chakra UI +- MUI (Material UI) +- Mantine + +**Rationale**: + +| Feature | shadcn/ui | NextUI | Chakra UI | MUI | +|---------|-----------|--------|-----------|-----| +| Own the code | Yes | No | No | No | +| Bundle size | Minimal | Medium | Medium | Large | +| Customization | Full | Medium | Good | Complex | +| Accessibility | Excellent | Good | Excellent | Good | +| Tailwind native | Yes | Yes | No | No | +| Learning curve | Low | Low | Medium | High | + +**Key Benefits**: +1. **Code Ownership**: Copy/paste, not a dependency +2. **Full Customization**: Modify anything +3. **Accessibility**: Built on Radix primitives +4. **Tailwind Native**: Works seamlessly +5. **Contributor Friendly**: Standard React components + +**Example Component**: +```typescript +// packages/ui/src/components/button.tsx +import * as React from "react"; +import { Slot } from "@radix-ui/react-slot"; +import { cva, type VariantProps } from "class-variance-authority"; +import { cn } from "../lib/utils"; + +const buttonVariants = cva( + "inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50", + { + variants: { + variant: { + default: "bg-primary text-primary-foreground hover:bg-primary/90", + destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90", + outline: "border border-input bg-background hover:bg-accent hover:text-accent-foreground", + secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80", + ghost: "hover:bg-accent hover:text-accent-foreground", + link: "text-primary underline-offset-4 hover:underline", + }, + size: { + default: "h-10 px-4 py-2", + sm: "h-9 rounded-md px-3", + lg: "h-11 rounded-md px-8", + icon: "h-10 w-10", + }, + }, + defaultVariants: { + variant: "default", + size: "default", + }, + } +); + +export interface ButtonProps + extends React.ButtonHTMLAttributes, + VariantProps { + asChild?: boolean; +} + +const Button = React.forwardRef( + ({ className, variant, size, asChild = false, ...props }, ref) => { + const Comp = asChild ? Slot : "button"; + return ( + + ); + } +); +Button.displayName = "Button"; + +export { Button, buttonVariants }; +``` + + +### TailwindCSS 3.4+ + +**Choice**: TailwindCSS for styling + +**Alternatives Considered**: +- Styled Components (partially used in current) +- CSS Modules +- Emotion +- Vanilla CSS + +**Rationale**: + +| Feature | TailwindCSS | Styled Components | CSS Modules | +|---------|-------------|-------------------|-------------| +| Bundle size | Minimal (purged) | Runtime | Zero | +| DX | Excellent | Good | Medium | +| Design system | Built-in | Manual | Manual | +| Dark mode | Built-in | Manual | Manual | +| Responsiveness | Built-in | Manual | Manual | +| Type safety | With plugins | Partial | No | + +**Key Benefits**: +1. **Utility-First**: Fast iteration +2. **Design Tokens**: Consistent spacing, colors +3. **Dark Mode**: Built-in support +4. **Responsive**: Mobile-first utilities +5. **Purging**: Only ships used CSS +6. **Industry Standard**: Widely known + +**Configuration**: +```typescript +// tailwind.config.ts +import type { Config } from "tailwindcss"; + +const config: Config = { + darkMode: "class", + content: [ + "./app/**/*.{ts,tsx}", + "./components/**/*.{ts,tsx}", + "../../packages/ui/src/**/*.{ts,tsx}", + ], + theme: { + extend: { + colors: { + border: "hsl(var(--border))", + input: "hsl(var(--input))", + ring: "hsl(var(--ring))", + background: "hsl(var(--background))", + foreground: "hsl(var(--foreground))", + primary: { + DEFAULT: "hsl(var(--primary))", + foreground: "hsl(var(--primary-foreground))", + }, + secondary: { + DEFAULT: "hsl(var(--secondary))", + foreground: "hsl(var(--secondary-foreground))", + }, + destructive: { + DEFAULT: "hsl(var(--destructive))", + foreground: "hsl(var(--destructive-foreground))", + }, + muted: { + DEFAULT: "hsl(var(--muted))", + foreground: "hsl(var(--muted-foreground))", + }, + accent: { + DEFAULT: "hsl(var(--accent))", + foreground: "hsl(var(--accent-foreground))", + }, + // Algorithm-specific colors + compare: "hsl(var(--compare))", + swap: "hsl(var(--swap))", + sorted: "hsl(var(--sorted))", + pivot: "hsl(var(--pivot))", + visited: "hsl(var(--visited))", + current: "hsl(var(--current))", + found: "hsl(var(--found))", + }, + animation: { + "slide-in": "slideIn 0.3s ease-out", + "fade-in": "fadeIn 0.2s ease-out", + "scale-in": "scaleIn 0.2s ease-out", + "bar-swap": "barSwap 0.3s ease-in-out", + }, + keyframes: { + slideIn: { + from: { transform: "translateY(-10px)", opacity: "0" }, + to: { transform: "translateY(0)", opacity: "1" }, + }, + fadeIn: { + from: { opacity: "0" }, + to: { opacity: "1" }, + }, + scaleIn: { + from: { transform: "scale(0.95)", opacity: "0" }, + to: { transform: "scale(1)", opacity: "1" }, + }, + barSwap: { + "0%": { transform: "translateX(0)" }, + "50%": { transform: "translateY(-10px)" }, + "100%": { transform: "translateX(var(--swap-distance))" }, + }, + }, + }, + }, + plugins: [require("tailwindcss-animate")], +}; + +export default config; +``` + + +## Animations and Visualizations + +### Framer Motion + +**Choice**: Framer Motion for UI animations + +**Alternatives Considered**: +- React Spring +- GSAP +- CSS animations only +- Motion One + +**Rationale**: + +| Feature | Framer Motion | GSAP | React Spring | Motion One | +|---------|---------------|------|--------------|------------| +| React integration | Native | Wrapper | Native | Native | +| Bundle size | Medium (20kb) | Large (60kb) | Medium | Small (3kb) | +| Learning curve | Low | Medium | Medium | Low | +| Layout animations | Excellent | Manual | Limited | No | +| Gestures | Built-in | Plugin | Limited | No | +| SSR | Yes | Manual | Yes | Yes | + +**Key Benefits**: +1. **Declarative**: Animate with props +2. **Layout Animations**: `layout` prop magic +3. **Gestures**: Drag, tap, hover built-in +4. **Exit Animations**: `AnimatePresence` +5. **Spring Physics**: Natural motion + +**Use Cases in OpenDSA**: +```typescript +// Page transitions + + {children} + + +// Control panel animations + + Play + + +// Sidebar toggle + + {/* Sidebar content */} + +``` + + +### D3.js + +**Choice**: D3.js for complex data visualizations + +**Alternatives Considered**: +- Chart.js +- Recharts +- Visx +- Pure SVG/Canvas + +**Rationale**: + +| Feature | D3.js | Recharts | Visx | Chart.js | +|---------|-------|----------|------|----------| +| Flexibility | Full | Limited | Full | Limited | +| Learning curve | High | Low | Medium | Low | +| Custom visualizations | Excellent | Poor | Good | Poor | +| Animation control | Full | Limited | Good | Limited | +| SVG manipulation | Native | Abstracted | Native | Canvas | + +**Key Benefits**: +1. **Full Control**: Build any visualization +2. **Data Binding**: Efficient updates +3. **Transitions**: Smooth, customizable +4. **Scales**: Map data to visual properties +5. **Existing Knowledge**: Already used in current project + +**Use Cases in OpenDSA**: +- Graph algorithms (BFS, DFS, Dijkstra) +- Tree visualizations (BST, traversals) +- Complex sorting visualizations (bars, with animations) +- Linked list visualizations + +**Example**: +```typescript +// Graph visualization with D3 +import * as d3 from 'd3'; + +function renderGraph(container: HTMLElement, nodes: Node[], edges: Edge[]) { + const svg = d3.select(container) + .append('svg') + .attr('width', '100%') + .attr('height', '100%'); + + const simulation = d3.forceSimulation(nodes) + .force('link', d3.forceLink(edges).id(d => d.id)) + .force('charge', d3.forceManyBody().strength(-300)) + .force('center', d3.forceCenter(width / 2, height / 2)); + + // Render edges + const link = svg.selectAll('.link') + .data(edges) + .join('line') + .attr('class', 'link') + .attr('stroke', '#999'); + + // Render nodes + const node = svg.selectAll('.node') + .data(nodes) + .join('circle') + .attr('class', 'node') + .attr('r', 20) + .attr('fill', d => d.visited ? 'var(--visited)' : 'var(--default)'); + + // Update positions on tick + simulation.on('tick', () => { + link + .attr('x1', d => d.source.x) + .attr('y1', d => d.source.y) + .attr('x2', d => d.target.x) + .attr('y2', d => d.target.y); + + node + .attr('cx', d => d.x) + .attr('cy', d => d.y); + }); +} +``` + + +### Canvas API + +**Choice**: Canvas API for high-performance array visualizations + +**Rationale**: +- **Performance**: Handle 100+ elements at 60fps +- **Control**: Pixel-level manipulation +- **Memory**: Lower memory usage than SVG for many elements + +**Use Cases**: +- Large array sorting visualizations (>50 elements) +- Performance-critical animations +- Real-time data updates + +**Example**: +```typescript +// High-performance array visualization +function renderArray( + canvas: HTMLCanvasElement, + array: number[], + highlights: Map +) { + const ctx = canvas.getContext('2d')!; + const { width, height } = canvas; + const barWidth = width / array.length; + const maxValue = Math.max(...array); + + ctx.clearRect(0, 0, width, height); + + array.forEach((value, index) => { + const barHeight = (value / maxValue) * (height - 20); + const x = index * barWidth; + const y = height - barHeight; + + // Set color based on state + ctx.fillStyle = highlights.get(index) || 'hsl(var(--primary))'; + ctx.fillRect(x + 1, y, barWidth - 2, barHeight); + + // Draw value label for small arrays + if (array.length <= 30) { + ctx.fillStyle = 'hsl(var(--foreground))'; + ctx.font = '12px sans-serif'; + ctx.textAlign = 'center'; + ctx.fillText(value.toString(), x + barWidth / 2, y - 5); + } + }); +} +``` + + +## State Management + +### Zustand + +**Choice**: Zustand for global state + +**Alternatives Considered**: +- Redux Toolkit (current) +- Jotai +- Recoil +- React Context + +**Rationale**: + +| Feature | Zustand | Redux Toolkit | Jotai | Context | +|---------|---------|---------------|-------|---------| +| Bundle size | 1kb | 11kb | 2kb | 0kb | +| Boilerplate | Minimal | Medium | Minimal | Medium | +| Learning curve | Low | Medium | Low | Low | +| DevTools | Yes | Excellent | Yes | Limited | +| TypeScript | Excellent | Good | Excellent | Good | +| Middleware | Yes | Yes | Limited | Manual | + +**Key Benefits**: +1. **Simplicity**: Minimal boilerplate +2. **Performance**: Selective re-renders out of box +3. **TypeScript**: Great type inference +4. **Flexibility**: No providers required +5. **Size**: Tiny bundle impact + +**Store Structure**: +```typescript +// Visualization store +interface VisualizerStore { + // State + visualizerId: string | null; + data: unknown; + steps: AnimationStep[]; + currentStep: number; + isPlaying: boolean; + speed: number; + + // Actions + setVisualizer: (id: string) => void; + setData: (data: unknown) => void; + generateSteps: () => void; + play: () => void; + pause: () => void; + reset: () => void; + stepForward: () => void; + stepBackward: () => void; + setSpeed: (speed: number) => void; +} + +// Preferences store (persisted) +interface PreferencesStore { + theme: 'light' | 'dark' | 'system'; + editorVisible: boolean; + sidebarOpen: boolean; + keyboardShortcutsEnabled: boolean; + + setTheme: (theme: 'light' | 'dark' | 'system') => void; + toggleEditor: () => void; + toggleSidebar: () => void; + toggleKeyboardShortcuts: () => void; +} +``` + + +## Code Editor + +### Monaco Editor + +**Choice**: Monaco Editor (same as VS Code) + +**Alternatives Considered**: +- CodeMirror 6 +- Ace Editor +- Prism.js (display only) + +**Rationale**: + +| Feature | Monaco | CodeMirror 6 | Ace | +|---------|--------|--------------|-----| +| IntelliSense | Full | Plugin | Limited | +| Bundle size | Large (2MB) | Medium | Medium | +| Features | Complete | Good | Good | +| Familiarity | VS Code | Learning | Old | +| TypeScript | Native | Plugin | Plugin | + +**Key Benefits**: +1. **VS Code Experience**: Familiar to developers +2. **IntelliSense**: Autocomplete, errors +3. **TypeScript**: Native support +4. **Themes**: VS Code themes work + +**Configuration**: +```typescript +// Monaco editor setup +import { Editor, OnMount } from '@monaco-editor/react'; + +interface CodeEditorProps { + code: string; + language: string; + highlightLines?: number[]; + readOnly?: boolean; + onChange?: (value: string) => void; +} + +export function CodeEditor({ + code, + language, + highlightLines = [], + readOnly = false, + onChange, +}: CodeEditorProps) { + const handleMount: OnMount = (editor, monaco) => { + // Configure editor + editor.updateOptions({ + minimap: { enabled: false }, + lineNumbers: 'on', + scrollBeyondLastLine: false, + readOnly, + fontSize: 14, + }); + + // Highlight lines + if (highlightLines.length > 0) { + editor.deltaDecorations([], highlightLines.map(line => ({ + range: new monaco.Range(line, 1, line, 1), + options: { + isWholeLine: true, + className: 'highlighted-line', + glyphMarginClassName: 'highlighted-glyph', + }, + }))); + } + }; + + return ( + onChange?.(value ?? '')} + theme="vs-dark" + /> + ); +} +``` + + +## Testing + +### Vitest + +**Choice**: Vitest for unit testing + +**Alternatives Considered**: +- Jest +- Node test runner +- Mocha + +**Rationale**: + +| Feature | Vitest | Jest | +|---------|--------|------| +| Speed | Fast (native ESM) | Slower | +| Vite integration | Native | Manual | +| Watch mode | Excellent | Good | +| API compatibility | Jest-compatible | - | +| TypeScript | Native | Plugin | + +**Configuration**: +```typescript +// vitest.config.ts +import { defineConfig } from 'vitest/config'; +import react from '@vitejs/plugin-react'; + +export default defineConfig({ + plugins: [react()], + test: { + environment: 'jsdom', + globals: true, + setupFiles: ['./test/setup.ts'], + coverage: { + provider: 'v8', + reporter: ['text', 'json', 'html'], + exclude: ['node_modules/', 'test/'], + }, + }, +}); +``` + + +### Playwright + +**Choice**: Playwright for E2E testing + +**Alternatives Considered**: +- Cypress +- Puppeteer +- TestCafe + +**Rationale**: + +| Feature | Playwright | Cypress | +|---------|------------|---------| +| Browsers | All | Chrome, Firefox, Edge | +| Speed | Faster | Slower | +| Auto-wait | Better | Good | +| Parallelism | Built-in | Paid | +| Cross-origin | Yes | Limited | + +**Configuration**: +```typescript +// playwright.config.ts +import { defineConfig, devices } from '@playwright/test'; + +export default defineConfig({ + testDir: './e2e', + fullyParallel: true, + forbidOnly: !!process.env.CI, + retries: process.env.CI ? 2 : 0, + workers: process.env.CI ? 1 : undefined, + reporter: 'html', + use: { + baseURL: 'http://localhost:3000', + trace: 'on-first-retry', + }, + projects: [ + { name: 'chromium', use: { ...devices['Desktop Chrome'] } }, + { name: 'firefox', use: { ...devices['Desktop Firefox'] } }, + { name: 'webkit', use: { ...devices['Desktop Safari'] } }, + ], + webServer: { + command: 'pnpm dev', + url: 'http://localhost:3000', + reuseExistingServer: !process.env.CI, + }, +}); +``` + + +## Documentation + +### Nextra + +**Choice**: Nextra for documentation site + +**Alternatives Considered**: +- Docusaurus +- Fumadocs +- VitePress +- GitBook + +**Rationale**: + +| Feature | Nextra | Docusaurus | Fumadocs | +|---------|--------|------------|----------| +| Framework | Next.js | React | Next.js | +| Setup | Simple | Medium | Simple | +| MDX | Native | Native | Native | +| Search | Built-in | Plugin | Built-in | +| Customization | Full | Good | Full | + +**Key Benefits**: +1. **Next.js Native**: Same stack as apps +2. **MDX**: React components in docs +3. **Search**: Full-text search built-in +4. **Theme**: Beautiful default theme +5. **Simple**: Minimal configuration + + +## Deployment + +### Vercel + +**Choice**: Vercel for hosting + +**Alternatives Considered**: +- Netlify (current) +- AWS Amplify +- Cloudflare Pages +- Self-hosted + +**Rationale**: + +| Feature | Vercel | Netlify | Cloudflare | +|---------|--------|---------|------------| +| Next.js support | Native | Good | Good | +| Preview deployments | Yes | Yes | Yes | +| Edge functions | Native | Beta | Native | +| Turborepo caching | Native | No | No | +| Pricing | Free tier | Free tier | Free tier | + +**Key Benefits**: +1. **Next.js Creator**: Best Next.js support +2. **Preview Deployments**: Per-PR previews +3. **Turborepo Integration**: Remote caching +4. **Zero Config**: Automatic detection +5. **Free for OSS**: Open source program + + +### GitHub Actions + +**Workflows**: + +1. **CI (ci.yml)**: Runs on every PR + - Lint + - Type check + - Unit tests + - Build verification + +2. **Release (release.yml)**: On merge to main + - Deploy to production + - Create release notes + - Update changelog + +3. **Dependency Update**: Weekly + - Dependabot PRs + - Security scanning + + +## Development Tools + +### ESLint + Prettier + +**Configuration**: +```javascript +// eslint.config.js +import eslint from '@eslint/js'; +import tseslint from 'typescript-eslint'; +import react from 'eslint-plugin-react'; +import reactHooks from 'eslint-plugin-react-hooks'; + +export default tseslint.config( + eslint.configs.recommended, + ...tseslint.configs.strictTypeChecked, + { + plugins: { + react, + 'react-hooks': reactHooks, + }, + rules: { + 'react-hooks/rules-of-hooks': 'error', + 'react-hooks/exhaustive-deps': 'warn', + '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }], + }, + } +); +``` + + +## Comparison with Current Stack + +| Aspect | Current (ds-algo-deck) | New (OpenDSA) | Reason for Change | +|--------|------------------------|----------------|-------------------| +| **Framework** | React + Vite | Next.js 14 | SSR, SEO, file routing, Vercel integration | +| **Language** | JavaScript | TypeScript | Type safety, better DX, documentation | +| **Package Manager** | npm | pnpm | Speed, disk efficiency, strict mode | +| **Build** | Single app | Turborepo | Code sharing, independent deployments | +| **UI Library** | NextUI | shadcn/ui | Code ownership, customization, smaller bundle | +| **State** | Redux Toolkit | Zustand | Simplicity, smaller bundle, less boilerplate | +| **Styling** | Tailwind + Styled Components | Tailwind only | Consistency, simpler mental model | +| **Animations** | Framer Motion + D3 | Same + Canvas | Add Canvas for performance | +| **Testing** | None | Vitest + Playwright | Quality assurance, contributor confidence | +| **Docs** | Markdown files | Nextra | Professional documentation site | +| **Deploy** | Netlify | Vercel | Better Next.js support, Turborepo integration | +| **CI/CD** | Manual | GitHub Actions | Automation, quality gates | + + +## Summary + +The OpenDSA tech stack is carefully chosen to provide: + +1. **Modern Foundation**: Next.js + TypeScript + pnpm +2. **Scalable Architecture**: Turborepo monorepo +3. **Beautiful UI**: shadcn/ui + TailwindCSS +4. **Smooth Animations**: Framer Motion + D3.js + Canvas +5. **Simple State**: Zustand +6. **Quality Assurance**: Vitest + Playwright +7. **Great DX**: ESLint + Prettier + TypeScript +8. **Easy Deployment**: Vercel + GitHub Actions + +This stack balances **developer experience**, **performance**, **maintainability**, and **contributor friendliness** - all essential for a successful open-source project. diff --git a/apps/docs/app/roadmap/page.mdx b/apps/docs/app/roadmap/page.mdx deleted file mode 100644 index 395d1c6..0000000 --- a/apps/docs/app/roadmap/page.mdx +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: Roadmap ---- - -This page will be migrated from `apps/docs/internal/OPENALGO_ROADMAP.md`. - diff --git a/apps/docs/app/sponsors/page.mdx b/apps/docs/app/sponsors/page.mdx new file mode 100644 index 0000000..417b464 --- /dev/null +++ b/apps/docs/app/sponsors/page.mdx @@ -0,0 +1,117 @@ +# Support OpenDSA + +OpenDSA is 100% free and open source. We believe everyone should have access to quality algorithm visualizations for learning. No paywalls. No premium tiers. Just pure, open education. + +## Why Support Us? + +We're committed to keeping OpenDSA free forever. Your support helps us: + +- **Host reliable infrastructure** - Fast servers, CDN, and uptime +- **Develop new features** - More algorithms, better visualizations +- **Maintain the project** - Bug fixes, security updates, documentation +- **Grow the community** - Events, educational content, outreach + +## Current Hosting + +OpenDSA is currently hosted on **Vercel's free tier**. As the project grows, we may need to scale to handle more traffic and features. + + +## How to Support + +### GitHub Sponsors (Coming Soon) +Support the maintainers directly through GitHub Sponsors. Every contribution helps keep the project alive. + +**Setup:** [github.com/sponsors](https://github.com/sponsors) - Account pending + +### Open Collective (Coming Soon) +For transparent, community-managed funding. All expenses and contributions are public. + +**Setup:** [opencollective.com](https://opencollective.com) - Account pending + +### Buy Me a Coffee (Coming Soon) +Quick one-time donations to fuel late-night coding sessions. + +**Setup:** [buymeacoffee.com](https://www.buymeacoffee.com) - Account pending + +### Ko-fi (Coming Soon) +Another great platform for one-time or monthly support. + +**Setup:** [ko-fi.com](https://ko-fi.com) - Account pending + +### Patreon (Coming Soon) +For those who want to support with monthly contributions. + +**Setup:** [patreon.com](https://www.patreon.com) - Account pending + + +## Corporate Sponsors + +Are you a company that values open source education? We'd love to partner with you! + +### Sponsorship Tiers + +#### Bronze - $50/month +- Logo on README +- Shoutout on social media + +#### Silver - $100/month +- Everything in Bronze +- Logo on website footer +- Monthly progress updates + +#### Gold - $250/month +- Everything in Silver +- Logo on homepage +- Featured in release notes + +#### Platinum - $500+/month +- Everything in Gold +- Custom feature prioritization +- Direct support channel + +**Interested?** Reach out at [GitHub Discussions](https://github.com/soloshun/opendsa/discussions) or email us at [solomoneshun373@gmail.com](mailto:solomoneshun373@gmail.com). + + +## Current Sponsors + +*Be the first to support OpenDSA!* + +No sponsors yet. Your logo could be here. + + +## Other Ways to Help + +Don't have the funds? No problem! You can still contribute: + +1. **Star the repo** - Helps with visibility +2. **Share on social media** - Spread the word +3. **Write a blog post** - Review or tutorial +4. **Report bugs** - Help us improve +5. **Submit PRs** - Code contributions welcome +6. **Answer questions** - Help others in discussions + + +## Transparency + +We believe in open finances. Once we set up Open Collective or similar platforms, all income and expenses will be publicly visible. + +**Current Monthly Costs:** \$0 (Vercel free tier)
+**Expected at Scale:** ~\$50-200/month (Pro hosting, domain, services) + + +## FAQ + +### Will OpenDSA ever have paid features? +No. OpenDSA will always be 100% free. Sponsorships help us maintain and improve the free platform. + +### Where does the money go? +Hosting costs, domain registration, and development tools. Any surplus goes toward bounties for contributors. + +### Can I sponsor anonymously? +Yes, most platforms support anonymous donations. + +### Is my donation tax-deductible? +Depends on your country and the platform used. Open Collective offers fiscal hosting which may provide tax benefits in some jurisdictions. + + +*Thank you for considering supporting OpenDSA. Every contribution, big or small, helps keep algorithm education free and accessible to everyone.* diff --git a/apps/docs/app/tech_stack/page.mdx b/apps/docs/app/tech_stack/page.mdx deleted file mode 100644 index a0c9fb5..0000000 --- a/apps/docs/app/tech_stack/page.mdx +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: Tech Stack ---- - -This page will be migrated from `apps/docs/internal/OPENALGO_TECH_STACK.md`. - diff --git a/apps/docs/app/technical/_meta.js b/apps/docs/app/technical/_meta.js new file mode 100644 index 0000000..47a289f --- /dev/null +++ b/apps/docs/app/technical/_meta.js @@ -0,0 +1,7 @@ +export default { + "implementation-status": 'Implementation Status', + "big-o": 'Big O Notation', + "data-structures": 'Data Structures', + algorithms: 'Algorithms', + visualizers: 'Visualizers' +} diff --git a/apps/docs/app/technical/algorithms/_meta.js b/apps/docs/app/technical/algorithms/_meta.js new file mode 100644 index 0000000..5eff340 --- /dev/null +++ b/apps/docs/app/technical/algorithms/_meta.js @@ -0,0 +1,4 @@ +export default { + "bubble-sort": "Bubble Sort", + "quick-sort": "Quick Sort" +} diff --git a/apps/docs/app/technical/algorithms/bubble-sort/page.mdx b/apps/docs/app/technical/algorithms/bubble-sort/page.mdx new file mode 100644 index 0000000..0743a73 --- /dev/null +++ b/apps/docs/app/technical/algorithms/bubble-sort/page.mdx @@ -0,0 +1,28 @@ +# Bubble Sort + +Bubble Sort is the simplest sorting algorithm that works by repeatedly swapping the adjacent elements if they are in the wrong order. + +## Complexity + +| Type | Complexity | +| :--- | :--- | +| **Time (Worst)** | $O(n^2)$ | +| **Time (Average)** | $O(n^2)$ | +| **Time (Best)** | $O(n)$ | +| **Space** | $O(1)$ | + +## Implementation + +```typescript +function bubbleSort(arr: number[]): number[] { + const n = arr.length; + for (let i = 0; i < n; i++) { + for (let j = 0; j < n - i - 1; j++) { + if (arr[j] > arr[j + 1]) { + [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]]; + } + } + } + return arr; +} +``` diff --git a/apps/docs/app/technical/algorithms/page.mdx b/apps/docs/app/technical/algorithms/page.mdx new file mode 100644 index 0000000..3f3af89 --- /dev/null +++ b/apps/docs/app/technical/algorithms/page.mdx @@ -0,0 +1,8 @@ +# Algorithms + +The current version of OpenDSA implements value-based algorithms to help users understand core computer science concepts. + +## Sorting Implementations + +- [Bubble Sort](/technical/algorithms/bubble-sort) +- [Quick Sort](/technical/algorithms/quick-sort) diff --git a/apps/docs/app/technical/algorithms/quick-sort/page.mdx b/apps/docs/app/technical/algorithms/quick-sort/page.mdx new file mode 100644 index 0000000..e79c253 --- /dev/null +++ b/apps/docs/app/technical/algorithms/quick-sort/page.mdx @@ -0,0 +1,17 @@ +# Quick Sort + +Quick Sort is an efficient, divide-and-conquer sorting algorithm. It works by selecting a 'pivot' element and partitioning the other elements into two sub-arrays according to whether they are less than or greater than the pivot. + +## Complexity + +| Type | Complexity | +| :--- | :--- | +| **Time (Worst)** | $O(n^2)$ | +| **Time (Average)** | $O(n \log n)$ | +| **Time (Best)** | $O(n \log n)$ | +| **Space** | $O(\log n)$ | + +## Concepts + +- **Pivot Selection**: Choosing the right pivot is crucial for performance. +- **Partitioning**: Reordering the array so that elements < pivot come before elements > pivot. diff --git a/apps/docs/app/technical/big-o/page.mdx b/apps/docs/app/technical/big-o/page.mdx new file mode 100644 index 0000000..4c689f2 --- /dev/null +++ b/apps/docs/app/technical/big-o/page.mdx @@ -0,0 +1,51 @@ +# Big O Notation + +Big O notation is a mathematical notation that describes the limiting behavior of a function when the argument tends towards a particular value or infinity. In computer science, it is used to classify algorithms according to how their run time or space requirements grow as the input size grows. + +## Common Time Complexities + +| Notation | Name | Description | Example | +| :--- | :--- | :--- | :--- | +| **O(1)** | Constant | Execution time is independent of input size. | Accessing array index | +| **O(log n)** | Logarithmic | Time grows logarithmically with input size. | Binary Search | +| **O(n)** | Linear | Time grows linearly with input size. | Linear Search | +| **O(n log n)** | Linearithmic | Faster than quadratic but slower than linear. | Merge Sort, Quick Sort | +| **O(n²)** | Quadratic | Time grows with the square of the input size. | Bubble Sort | +| **O(2ⁿ)** | Exponential | Time doubles with each addition to input. | Recursive Fibonacci | +| **O(n!)** | Factorial | Growth is factorial based on input size. | Traveling Salesperson | + +## Why it Matters + +Understanding Big O helps developers: +- Predict performance at scale. +- Choose the right algorithm for the job. +- Identify bottlenecks in code. + +### Visualizing Growth + +```mermaid +graph LR + A["Input Size (n)"] --> B{Complexity} + B -->|O 1| C[Instant] + B -->|O log n| D[Fast] + B -->|O n| E[Manageable] + B -->|O n^2| F[Slow] + B -->|O 2^n| G["Unusable for large n"] +``` + +### Complexity Chart + +```mermaid +xychart-beta + title "Time Complexity Growth" + x-axis "Input Size (n)" [1, 2, 4, 8, 16] + y-axis "Operations" 0 --> 300 + line [1, 1, 1, 1, 1] + line [1, 2, 4, 8, 16] + line [1, 4, 16, 64, 256] +``` +
+
O(1)
+
O(n)
+
O(n²)
+
diff --git a/apps/docs/app/technical/data-structures/_meta.js b/apps/docs/app/technical/data-structures/_meta.js new file mode 100644 index 0000000..5bbe0bc --- /dev/null +++ b/apps/docs/app/technical/data-structures/_meta.js @@ -0,0 +1,4 @@ +export default { + arrays: "Arrays", + trees: "Trees" +} diff --git a/apps/docs/app/technical/data-structures/arrays/page.mdx b/apps/docs/app/technical/data-structures/arrays/page.mdx new file mode 100644 index 0000000..c6c9cb0 --- /dev/null +++ b/apps/docs/app/technical/data-structures/arrays/page.mdx @@ -0,0 +1,18 @@ +# Arrays + +An array is a collection of items stored at contiguous memory locations. It is one of the simplest and most widely used data structures. + +## Complexity + +| Operation | Complexity | +| :--- | :--- | +| **Access** | $O(1)$ | +| **Search** | $O(n)$ | +| **Insertion** | $O(n)$ | +| **Deletion** | $O(n)$ | + +## Visualization + +In OpenDSA, arrays are visualized as a row of bars or boxes. +- **Highlight**: Change color to indicate comparison or access. +- **Swap**: Animate position exchange. diff --git a/apps/docs/app/technical/data-structures/page.mdx b/apps/docs/app/technical/data-structures/page.mdx new file mode 100644 index 0000000..1f92f3a --- /dev/null +++ b/apps/docs/app/technical/data-structures/page.mdx @@ -0,0 +1,8 @@ +# Data Structures + +Core data structures implemented in OpenDSA with interactive visualizations. + +## Available Data Structures + +- [Arrays](/technical/data-structures/arrays) - Linear collection of elements +- [Trees](/technical/data-structures/trees) - Hierarchical node-based structures diff --git a/apps/docs/app/technical/data-structures/trees/page.mdx b/apps/docs/app/technical/data-structures/trees/page.mdx new file mode 100644 index 0000000..7a1dc2a --- /dev/null +++ b/apps/docs/app/technical/data-structures/trees/page.mdx @@ -0,0 +1,12 @@ +# Trees + +Trees are hierarchical data structures consisting of nodes connected by edges. + +## Binary Search Tree (BST) + +A binary tree where the key in each node is greater than all keys in the left subtree and less than all keys in the right subtree. + +### Properties +- **Root**: Topmost node. +- **Leaf**: Node with no children. +- **Height**: Number of edges on the longest path from node to leaf. diff --git a/apps/docs/app/technical/implementation-status/page.mdx b/apps/docs/app/technical/implementation-status/page.mdx new file mode 100644 index 0000000..53b87c0 --- /dev/null +++ b/apps/docs/app/technical/implementation-status/page.mdx @@ -0,0 +1,185 @@ +# Implementation Status + +This page tracks the implementation progress of all algorithms and data structures in OpenDSA. Want to contribute? Pick any item marked with ○ and start building! + +## Progress Overview + +**Total Items:** 70+ +**Implemented:** 4 +**In Progress:** Community-driven development + +--- + +## Data Structures + +### Arrays +| Topic | Status | +|-------|--------| +| Basic Array Operations | ✅ Implemented | +| Dynamic Arrays | ○ Pending | +| Multi-dimensional Arrays | ○ Pending | + +### Linked Lists +| Topic | Status | +|-------|--------| +| Singly Linked List | ○ Pending | +| Doubly Linked List | ○ Pending | +| Circular Linked List | ○ Pending | + +### Stacks +| Topic | Status | +|-------|--------| +| Basic Stack Operations | ○ Pending | +| Applications (Balancing Parentheses) | ○ Pending | + +### Queues +| Topic | Status | +|-------|--------| +| Basic Queue | ○ Pending | +| Circular Queue | ○ Pending | +| Priority Queue | ○ Pending | +| Deque (Double-Ended Queue) | ○ Pending | + +### Trees +| Topic | Status | +|-------|--------| +| Binary Tree | ○ Pending | +| Binary Search Tree (BST) | ○ Pending | +| AVL Tree (Self-Balancing BST) | ○ Pending | +| Red-Black Tree | ○ Pending | +| Segment Tree | ○ Pending | +| Trie | ○ Pending | +| B-Tree and B+ Tree | ○ Pending | + +### Heaps +| Topic | Status | +|-------|--------| +| Min Heap | ○ Pending | +| Max Heap | ○ Pending | +| Fibonacci Heap | ○ Pending | + +### Graphs +| Topic | Status | +|-------|--------| +| Directed/Undirected Graphs | ○ Pending | +| Weighted/Unweighted Graphs | ○ Pending | +| Adjacency Matrix | ○ Pending | +| Adjacency List | ○ Pending | +| Edge List | ○ Pending | + +### Hashing +| Topic | Status | +|-------|--------| +| Hash Tables | ○ Pending | +| Hash Maps | ○ Pending | +| Open Addressing | ○ Pending | +| Separate Chaining | ○ Pending | + +--- + +## Algorithms + +### Sorting (Easy) +| Topic | Status | +|-------|--------| +| Bubble Sort | ✅ Implemented | +| Selection Sort | ○ Pending | +| Insertion Sort | ○ Pending | + +### Sorting (Intermediate) +| Topic | Status | +|-------|--------| +| Merge Sort | ○ Pending | +| Quick Sort | ○ Pending | + +### Sorting (Advanced) +| Topic | Status | +|-------|--------| +| Heap Sort | ○ Pending | +| Counting Sort | ○ Pending | +| Radix Sort | ○ Pending | +| Bucket Sort | ○ Pending | + +### Searching +| Topic | Status | +|-------|--------| +| Linear Search | ✅ Implemented | +| Binary Search | ○ Pending | +| Jump Search | ○ Pending | +| Exponential Search | ○ Pending | + +### Graph Algorithms +| Topic | Status | +|-------|--------| +| Breadth-First Search (BFS) | ○ Pending | +| Depth-First Search (DFS) | ○ Pending | +| Dijkstra's Algorithm | ○ Pending | +| Floyd-Warshall | ○ Pending | +| Bellman-Ford | ○ Pending | +| Prim's MST | ○ Pending | +| Kruskal's MST | ○ Pending | +| A* Search | ○ Pending | + +### Dynamic Programming +| Topic | Status | +|-------|--------| +| Fibonacci | ○ Pending | +| Coin Change (Minimum Coins) | ○ Pending | +| Longest Common Subsequence (LCS) | ○ Pending | +| Knapsack Problem | ○ Pending | +| Matrix Chain Multiplication | ○ Pending | + +### Backtracking +| Topic | Status | +|-------|--------| +| Rat in a Maze | ○ Pending | +| N-Queens Problem | ○ Pending | +| Knight's Tour | ○ Pending | +| Sudoku Solver | ○ Pending | + +### Greedy Algorithms +| Topic | Status | +|-------|--------| +| Activity Selection | ○ Pending | +| Fractional Knapsack | ○ Pending | +| Huffman Coding | ○ Pending | +| Job Sequencing | ○ Pending | + +### Miscellaneous Algorithms +| Topic | Status | +|-------|--------| +| Sieve of Eratosthenes (Prime Numbers) | ○ Pending | +| Euclidean Algorithm (GCD) | ○ Pending | +| KMP Pattern Matching | ○ Pending | +| Rabin-Karp Algorithm | ○ Pending | +| Bit Manipulation | ○ Pending | +| Randomized Algorithms (QuickSelect) | ○ Pending | + +--- + +## Future Plans + +### Machine Learning Visualizers (Coming Soon) +- Linear Regression +- Logistic Regression +- Decision Trees +- K-Means Clustering +- Neural Network Forward/Backward Pass +- Gradient Descent + +### Deep Learning Visualizers (Coming Soon) +- Convolutional Neural Networks (CNN) +- Recurrent Neural Networks (RNN) +- Transformer Architecture +- Attention Mechanisms + +--- + +## How to Contribute + +1. **Pick an item** - Choose any pending (○) algorithm or data structure +2. **Create an issue** - Let the community know you're working on it +3. **Build the visualizer** - Follow our [architecture guide](/project/architecture) +4. **Submit a PR** - Reference the issue in your pull request + +See our [Contributing Guide](/contributing) for detailed instructions. diff --git a/apps/docs/app/technical/page.mdx b/apps/docs/app/technical/page.mdx new file mode 100644 index 0000000..2c43071 --- /dev/null +++ b/apps/docs/app/technical/page.mdx @@ -0,0 +1,9 @@ +# Technical Documentation + +Detailed documentation of the internal implementation of OpenDSA. + +## Sections + +- [Data Structures](/technical/data-structures): Implementation details of core data structures. +- [Algorithms](/technical/algorithms): Documentation of sorting, searching, graph, and tree algorithms. +- [Visualizers](/technical/visualizers): Architecture of the visualization engine and plugin system. diff --git a/apps/docs/app/technical/visualizers/_meta.js b/apps/docs/app/technical/visualizers/_meta.js new file mode 100644 index 0000000..c814c72 --- /dev/null +++ b/apps/docs/app/technical/visualizers/_meta.js @@ -0,0 +1,4 @@ +export default { + architecture: "Architecture" +} + diff --git a/apps/docs/app/technical/visualizers/architecture/page.mdx b/apps/docs/app/technical/visualizers/architecture/page.mdx new file mode 100644 index 0000000..ef9d349 --- /dev/null +++ b/apps/docs/app/technical/visualizers/architecture/page.mdx @@ -0,0 +1,25 @@ +# Visualizer Architecture + +The core of OpenDSA is the extensible visualizer system. + +## Key Concepts + +### Plugin System +Each algorithm visualization is a "Plugin" that provides: +- **Meta**: Name, category, complexity. +- **Component**: The React component rendering the visualization. +- **Generator**: A function that generates animation steps. + +### Example: Bubble Sort +```typescript +export const BubbleSortPlugin: VisualizerPlugin = { + meta: { + id: "bubble-sort", + name: "Bubble Sort", + category: "sorting", + complexity: { time: { worst: "O(n²)", ... }, space: "O(1)" } + }, + component: BubbleSortRenderer, + generateSteps: generateBubbleSortSteps +}; +``` diff --git a/apps/docs/app/technical/visualizers/page.mdx b/apps/docs/app/technical/visualizers/page.mdx new file mode 100644 index 0000000..4528e55 --- /dev/null +++ b/apps/docs/app/technical/visualizers/page.mdx @@ -0,0 +1,7 @@ +# Visualizers + +The OpenDSA visualizer system powers all algorithm and data structure animations. + +## Documentation + +- [Architecture](/technical/visualizers/architecture) - Core visualizer architecture and plugin system diff --git a/apps/docs/internal/OPENALGO_ARCHITECTURE.md b/apps/docs/internal/OPENDSA_ARCHITECTURE.md similarity index 100% rename from apps/docs/internal/OPENALGO_ARCHITECTURE.md rename to apps/docs/internal/OPENDSA_ARCHITECTURE.md diff --git a/apps/docs/internal/OPENALGO_MIGRATION.md b/apps/docs/internal/OPENDSA_MIGRATION.md similarity index 100% rename from apps/docs/internal/OPENALGO_MIGRATION.md rename to apps/docs/internal/OPENDSA_MIGRATION.md diff --git a/apps/docs/internal/OPENALGO_ROADMAP.md b/apps/docs/internal/OPENDSA_ROADMAP.md similarity index 100% rename from apps/docs/internal/OPENALGO_ROADMAP.md rename to apps/docs/internal/OPENDSA_ROADMAP.md diff --git a/apps/docs/internal/OPENALGO_TECH_STACK.md b/apps/docs/internal/OPENDSA_TECH_STACK.md similarity index 100% rename from apps/docs/internal/OPENALGO_TECH_STACK.md rename to apps/docs/internal/OPENDSA_TECH_STACK.md diff --git a/apps/docs/next.config.mjs b/apps/docs/next.config.mjs index 54eff3a..17f832f 100644 --- a/apps/docs/next.config.mjs +++ b/apps/docs/next.config.mjs @@ -2,6 +2,9 @@ import nextra from 'nextra' const withNextra = nextra({ // Nextra-specific options can go here. + latex: true, + // This helps with code block parsing + defaultShowCopyCode: true, }) export default withNextra({ diff --git a/apps/docs/package.json b/apps/docs/package.json index 8a4bdba..7526f03 100644 --- a/apps/docs/package.json +++ b/apps/docs/package.json @@ -8,10 +8,12 @@ "start": "next start" }, "dependencies": { + "@theguild/remark-mermaid": "^0.3.0", + "katex": "^0.16.27", "next": "16.1.4", - "react": "19.2.3", - "react-dom": "19.2.3", "nextra": "^4.6.1", - "nextra-theme-docs": "^4.6.1" + "nextra-theme-docs": "^4.6.1", + "react": "19.2.3", + "react-dom": "19.2.3" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 99b5866..ace5cd6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -53,7 +53,7 @@ importers: version: 9.39.2(jiti@2.6.1) eslint-config-next: specifier: 16.1.4 - version: 16.1.4(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + version: 16.1.4(@typescript-eslint/parser@8.53.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) tailwindcss: specifier: ^4 version: 4.1.18 @@ -66,6 +66,9 @@ importers: '@theguild/remark-mermaid': specifier: ^0.3.0 version: 0.3.0(react@19.2.3) + katex: + specifier: ^0.16.27 + version: 0.16.27 next: specifier: 16.1.4 version: 16.1.4(@babel/core@7.28.6)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) @@ -132,7 +135,7 @@ importers: version: 9.39.2(jiti@2.6.1) eslint-config-next: specifier: 16.1.4 - version: 16.1.4(@typescript-eslint/parser@8.53.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + version: 16.1.4(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) tailwindcss: specifier: ^4 version: 4.1.18 From b96f7eb9c6adf0b06dae17545fdbf1c7a49c3421 Mon Sep 17 00:00:00 2001 From: Solo Shun <88045720+soloshun@users.noreply.github.com> Date: Sun, 25 Jan 2026 18:04:10 +0000 Subject: [PATCH 7/7] feat(web): add particles background, mobile responsive array-search animation, implementation checklist roadmap and git contributors ui --- apps/web/src/app/page.tsx | 2 + .../components/animations/array-search.tsx | 42 ++- .../sections/algorithm-checklist.tsx | 272 +++++++++++++++ apps/web/src/components/sections/footer.tsx | 5 +- apps/web/src/components/sections/header.tsx | 15 +- .../src/components/sections/open-source.tsx | 322 ++++++++++++++++-- .../components/ui/particles-background.tsx | 4 +- apps/web/src/lib/algorithms-data.ts | 205 +++++++++++ 8 files changed, 818 insertions(+), 49 deletions(-) create mode 100644 apps/web/src/components/sections/algorithm-checklist.tsx create mode 100644 apps/web/src/lib/algorithms-data.ts diff --git a/apps/web/src/app/page.tsx b/apps/web/src/app/page.tsx index a676623..f2fac72 100644 --- a/apps/web/src/app/page.tsx +++ b/apps/web/src/app/page.tsx @@ -4,6 +4,7 @@ import { Features } from "@/components/sections/features"; import { Architecture } from "@/components/sections/architecture"; import { TechStack } from "@/components/sections/tech-stack"; import { Roadmap } from "@/components/sections/roadmap"; +import { AlgorithmChecklist } from "@/components/sections/algorithm-checklist"; import { FAQ } from "@/components/sections/faq"; import { OpenSource } from "@/components/sections/open-source"; import { Footer } from "@/components/sections/footer"; @@ -24,6 +25,7 @@ export default function Home() { +
diff --git a/apps/web/src/components/animations/array-search.tsx b/apps/web/src/components/animations/array-search.tsx index 0285ed0..689a89b 100644 --- a/apps/web/src/components/animations/array-search.tsx +++ b/apps/web/src/components/animations/array-search.tsx @@ -3,17 +3,40 @@ import { motion } from "framer-motion"; import { useEffect, useState } from "react"; -const array = [2, 5, 8, 11, 12, 16, 23, 28, 38, 56, 60, 67, 72, 81, 91, 99, 123, 212]; +const DESKTOP_ARRAY = [2, 5, 8, 11, 12, 16, 23, 28, 38, 56, 60, 67, 72, 81, 91, 99, 123, 212]; +const MOBILE_ARRAY = [67, 69, 70, 77, 81, 98]; export function ArraySearch() { + const [array, setArray] = useState(DESKTOP_ARRAY); const target = 67; const [currentIndex, setCurrentIndex] = useState(-1); const [left, setLeft] = useState(0); - const [right, setRight] = useState(array.length - 1); + const [right, setRight] = useState(DESKTOP_ARRAY.length - 1); const [found, setFound] = useState(false); + // Handle responsive array useEffect(() => { + const handleResize = () => { + if (window.innerWidth < 768) { + setArray(MOBILE_ARRAY); + } else { + setArray(DESKTOP_ARRAY); + } + }; + + // Initial check + handleResize(); + + window.addEventListener("resize", handleResize); + return () => window.removeEventListener("resize", handleResize); + }, []); + + useEffect(() => { + let isCancelled = false; + const animateBinarySearch = async () => { + if (isCancelled) return; + setCurrentIndex(-1); setLeft(0); setRight(array.length - 1); @@ -23,6 +46,8 @@ export function ArraySearch() { let r = array.length - 1; while (l <= r) { + if (isCancelled) return; + const mid = Math.floor((l + r) / 2); setCurrentIndex(mid); setLeft(l); @@ -39,14 +64,19 @@ export function ArraySearch() { } } - await new Promise((resolve) => setTimeout(resolve, 2500)); + if (!isCancelled) { + await new Promise((resolve) => setTimeout(resolve, 2500)); + } }; const interval = setInterval(animateBinarySearch, 8000); animateBinarySearch(); - return () => clearInterval(interval); - }, []); + return () => { + isCancelled = true; + clearInterval(interval); + }; + }, [array]); // Restart animation when array changes return (
@@ -58,7 +88,7 @@ export function ArraySearch() { return ( t.implemented).length; + + return ( +
+ + + + {isOpen && ( + + {topics.map((item, index) => ( +
+ + + {index === topics.length - 1 ? "└─" : "├─"} + + {item.name} + + + {item.implemented ? ( + + ) : ( + + )} + +
+ ))} +
+ )} +
+
+ ); +} + +interface CategorySectionProps { + category: Category; + visible: boolean; + setVisible: (visible: boolean) => void; +} + +function CategorySection({ category, visible, setVisible }: CategorySectionProps) { + const { total, implemented } = getCategoryCount(category); + + return ( +
+ + + + {visible && ( + + {Object.entries(category.items).map(([topic, { topics }]) => ( + + ))} + + )} + +
+ ); +} + +export function AlgorithmChecklist() { + const [dsVisible, setDsVisible] = useState(true); + const [algoVisible, setAlgoVisible] = useState(true); + const { total, implemented } = getTotalCount(); + const progress = Math.round((implemented / total) * 100); + + return ( +
+ {/* Background */} +
+
+
+ + {/* Noise overlay */} +
+
+
+ +
+ {/* Section header */} + +
+ + / Implementation Roadmap +
+ +

+ THE AMBITIOUS LIST +

+ +

+ {total}+ algorithms and data structures. This is what we're building together. + Pick one and contribute, or suggest new ones. ML/DL, algorithms from other fields, and visualizers are all welcome. +

+ + {/* Progress bar */} +
+
+ Progress + {implemented}/{total} ({progress}%) +
+
+ +
+
+
+ + {/* Terminal-style container */} + + {/* Terminal header */} +
+
+
+
+
+
+
+ +
+ + opendsa@roadmap ~ $ + +
+ + {/* Terminal content */} +
+ {/* Header banner */} +
+
+ + OpenDSA Implementation Roadmap + +
+

+ Click on any category to expand/collapse +

+
+ + {/* Two columns */} +
+
+ +
+ +
+ +
+
+ + {/* Legend */} +
+ Status: + + Implemented + + + Pending + +
+ + {/* Cursor */} +
+ $ + +
+
+ + + {/* Future plans */} + +

+ Open to Contributions:{" "} + Machine Learning & Deep Learning Algorithm Visualizers etc... +

+
+
+
+ ); +} diff --git a/apps/web/src/components/sections/footer.tsx b/apps/web/src/components/sections/footer.tsx index 14cf254..9919bad 100644 --- a/apps/web/src/components/sections/footer.tsx +++ b/apps/web/src/components/sections/footer.tsx @@ -20,6 +20,7 @@ const footerLinks = { { label: "GitHub", href: "https://github.com/soloshun/opendsa" }, { label: "Discord", href: "#" }, { label: "Twitter", href: "#" }, + { label: "Support Us ☕", href: "https://docs.opendsa.dev/sponsors" }, ], }; @@ -46,10 +47,10 @@ export function Footer() { {/* Gradient from bottom */}
- + {/* Background */}
- + {/* Noise overlay */}
diff --git a/apps/web/src/components/sections/header.tsx b/apps/web/src/components/sections/header.tsx index 59b70c7..e453214 100644 --- a/apps/web/src/components/sections/header.tsx +++ b/apps/web/src/components/sections/header.tsx @@ -1,14 +1,19 @@ "use client"; import { motion } from "framer-motion"; -import { Github, Menu, X, Sun } from "lucide-react"; +import { + Github, + Menu, + X, + // Sun +} from "lucide-react"; import Link from "next/link"; import { useState } from "react"; const navLinks = [ { href: "#features", label: "Features" }, { href: "#roadmap", label: "Roadmap" }, - { href: "#open-source", label: "Open Source" }, + // { href: "#open-source", label: "Open Source" }, { href: "https://docs.opendsa.dev", label: "Docs", external: true }, ]; @@ -20,7 +25,7 @@ export function Header() { initial={{ y: -100, opacity: 0 }} animate={{ y: 0, opacity: 1 }} transition={{ duration: 0.6, ease: "easeOut" }} - className="fixed top-4 left-1/2 -translate-x-1/2 z-50 w-[95%] max-w-5xl" + className="fixed top-4 left-1/2 -translate-x-1/2 z-50 w-[95%] max-w-6xl" > {/* Main nav container - rounded pill style */}