diff --git a/package-lock.json b/package-lock.json
index eb9a8f1..cb93192 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -17,9 +17,11 @@
"@tailwindcss/vite": "^4.1.14",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
+ "framer-motion": "^12.23.24",
"lucide-react": "^0.545.0",
"react": "^19.1.1",
"react-dom": "^19.1.1",
+ "react-hot-toast": "^2.6.0",
"react-redux": "^9.2.0",
"react-router-dom": "^7.9.4",
"socket.io-client": "^4.8.1",
@@ -2572,7 +2574,6 @@
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
- "devOptional": true,
"license": "MIT"
},
"node_modules/debug": {
@@ -3130,6 +3131,33 @@
"url": "https://github.com/sponsors/rawify"
}
},
+ "node_modules/framer-motion": {
+ "version": "12.23.24",
+ "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.23.24.tgz",
+ "integrity": "sha512-HMi5HRoRCTou+3fb3h9oTLyJGBxHfW+HnNE25tAXOvVx/IvwMHK0cx7IR4a2ZU6sh3IX1Z+4ts32PcYBOqka8w==",
+ "license": "MIT",
+ "dependencies": {
+ "motion-dom": "^12.23.23",
+ "motion-utils": "^12.23.6",
+ "tslib": "^2.4.0"
+ },
+ "peerDependencies": {
+ "@emotion/is-prop-valid": "*",
+ "react": "^18.0.0 || ^19.0.0",
+ "react-dom": "^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@emotion/is-prop-valid": {
+ "optional": true
+ },
+ "react": {
+ "optional": true
+ },
+ "react-dom": {
+ "optional": true
+ }
+ }
+ },
"node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
@@ -3189,6 +3217,15 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/goober": {
+ "version": "2.1.18",
+ "resolved": "https://registry.npmjs.org/goober/-/goober-2.1.18.tgz",
+ "integrity": "sha512-2vFqsaDVIT9Gz7N6kAL++pLpp41l3PfDuusHcjnGLfR6+huZkl6ziX+zgVC3ZxpqWhzH6pyDdGrCeDhMIvwaxw==",
+ "license": "MIT",
+ "peerDependencies": {
+ "csstype": "^3.0.10"
+ }
+ },
"node_modules/graceful-fs": {
"version": "4.2.11",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
@@ -3667,6 +3704,21 @@
"node": ">= 18"
}
},
+ "node_modules/motion-dom": {
+ "version": "12.23.23",
+ "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.23.23.tgz",
+ "integrity": "sha512-n5yolOs0TQQBRUFImrRfs/+6X4p3Q4n1dUEqt/H58Vx7OW6RF+foWEgmTVDhIWJIMXOuNNL0apKH2S16en9eiA==",
+ "license": "MIT",
+ "dependencies": {
+ "motion-utils": "^12.23.6"
+ }
+ },
+ "node_modules/motion-utils": {
+ "version": "12.23.6",
+ "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.23.6.tgz",
+ "integrity": "sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ==",
+ "license": "MIT"
+ },
"node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
@@ -3847,6 +3899,23 @@
"react": "^19.2.0"
}
},
+ "node_modules/react-hot-toast": {
+ "version": "2.6.0",
+ "resolved": "https://registry.npmjs.org/react-hot-toast/-/react-hot-toast-2.6.0.tgz",
+ "integrity": "sha512-bH+2EBMZ4sdyou/DPrfgIouFpcRLCJ+HoCA32UoAYHn6T3Ur5yfcDCeSr5mwldl6pFOsiocmrXMuoCJ1vV8bWg==",
+ "license": "MIT",
+ "dependencies": {
+ "csstype": "^3.1.3",
+ "goober": "^2.1.16"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "react": ">=16",
+ "react-dom": ">=16"
+ }
+ },
"node_modules/react-redux": {
"version": "9.2.0",
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz",
diff --git a/package.json b/package.json
index d851ee4..8e82796 100644
--- a/package.json
+++ b/package.json
@@ -19,9 +19,11 @@
"@tailwindcss/vite": "^4.1.14",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
+ "framer-motion": "^12.23.24",
"lucide-react": "^0.545.0",
"react": "^19.1.1",
"react-dom": "^19.1.1",
+ "react-hot-toast": "^2.6.0",
"react-redux": "^9.2.0",
"react-router-dom": "^7.9.4",
"socket.io-client": "^4.8.1",
@@ -43,4 +45,4 @@
"tw-animate-css": "^1.4.0",
"vite": "^7.1.7"
}
-}
\ No newline at end of file
+}
diff --git a/src/App.jsx b/src/App.jsx
index 3c000cf..44174b0 100644
--- a/src/App.jsx
+++ b/src/App.jsx
@@ -1,8 +1,17 @@
import React from "react";
-import Homepage from "../src/pages/Homepage.jsx";
+import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
+import Homepage from "./pages/Homepage";
+import SortingPage from "./pages/sorting/SortingPage";
function App() {
- return ;
+ return (
+
+
+ } />
+ } />
+
+
+ );
}
export default App;
diff --git a/src/algorithms/sorting/selectionSort.js b/src/algorithms/sorting/selectionSort.js
new file mode 100644
index 0000000..86c2256
--- /dev/null
+++ b/src/algorithms/sorting/selectionSort.js
@@ -0,0 +1,26 @@
+// src/algorithms/sorting/selectionSort.js
+export function* selectionSort(array) {
+ const arr = [...array];
+ const n = arr.length;
+
+ for (let i = 0; i < n - 1; i++) {
+ let minIndex = i;
+ yield { type: "compare", indices: [i] };
+
+ for (let j = i + 1; j < n; j++) {
+ yield { type: "compare", indices: [minIndex, j] };
+
+ if (arr[j] < arr[minIndex]) {
+ minIndex = j;
+ yield { type: "min", index: minIndex };
+ }
+ }
+
+ if (minIndex !== i) {
+ [arr[i], arr[minIndex]] = [arr[minIndex], arr[i]];
+ yield { type: "swap", indices: [i, minIndex], array: [...arr] };
+ }
+ }
+
+ yield { type: "done", array: arr };
+}
diff --git a/src/components/sorting/SelectionSortVisualizer.jsx b/src/components/sorting/SelectionSortVisualizer.jsx
new file mode 100644
index 0000000..e788285
--- /dev/null
+++ b/src/components/sorting/SelectionSortVisualizer.jsx
@@ -0,0 +1,38 @@
+import React from "react";
+
+const COLORS = {
+ default: "bg-blue-500 shadow-[0_0_10px_#3b82f6]",
+ comparing: "bg-yellow-400 shadow-[0_0_12px_#facc15]",
+ min: "bg-green-500 shadow-[0_0_12px_#22c55e]",
+ swap: "bg-red-500 shadow-[0_0_12px_#ef4444]",
+};
+
+export default function SelectionSortVisualizer({ array, highlight }) {
+ const maxValue = Math.max(...array, 1); // Avoid division by zero
+ const containerHeight = 288; // px (matches h-72)
+
+ return (
+
+ {array.map((value, idx) => {
+ let color = COLORS.default;
+ if (highlight?.type === "compare" && highlight.indices?.includes(idx))
+ color = COLORS.comparing;
+ if (highlight?.type === "min" && highlight.index === idx)
+ color = COLORS.min;
+ if (highlight?.type === "swap" && highlight.indices?.includes(idx))
+ color = COLORS.swap;
+
+ // Normalize height relative to the maximum value
+ const height = Math.max((value / maxValue) * containerHeight, 15); // minimum 15px for visibility
+
+ return (
+
+ );
+ })}
+
+ );
+}
diff --git a/src/pages/Homepage.jsx b/src/pages/Homepage.jsx
index 8d82285..3146225 100644
--- a/src/pages/Homepage.jsx
+++ b/src/pages/Homepage.jsx
@@ -1,6 +1,8 @@
import React from "react";
import { ArrowRight, Github } from "lucide-react";
import sort from "../assets/sorting.png"
+import { useNavigate } from "react-router-dom";
+
const sections = [
{
@@ -8,7 +10,9 @@ const sections = [
description:
"Visualize step-by-step how sorting algorithms organize data efficiently.",
phase: "Phase 1 (MVP)",
- img: {sort},
+ img: "https://tamimehsan.github.io/AlgorithmVisualizer/images/sort.png?height=200&width=300",
+ link: "/sorting",
+ flag: false
},
{
title: "Searching Algorithms",
@@ -16,6 +20,8 @@ const sections = [
"Understand linear and binary search methods through live visualization.",
phase: "Phase 1 (MVP)",
img: "",
+ link: "/searching",
+ flag: true
},
{
title: "Pathfinding Algorithms",
@@ -23,6 +29,8 @@ const sections = [
"Watch how A*, Dijkstra and BFS explore grids to find the optimal path.",
phase: "Phase 1 (MVP)",
img: "",
+ link: "/pathfinding",
+ flag: true
},
{
title: "Graph Algorithms",
@@ -30,6 +38,8 @@ const sections = [
"Explore BFS, DFS, Kruskal’s, Prim’s, and more — all brought to life interactively.",
phase: "Phase 2",
img: "",
+ link: "/graphs",
+ flag: true
},
{
title: "Recursion & Backtracking",
@@ -37,6 +47,8 @@ const sections = [
"Visualize recursive calls and backtracking patterns like N-Queens or Sudoku.",
phase: "Phase 2",
img: "",
+ link: "/recursion",
+ flag: true
},
{
title: "Data Structures Visualization",
@@ -44,6 +56,8 @@ const sections = [
"Interactively understand stacks, queues, linked lists, trees, and heaps.",
phase: "Phase 2",
img: "",
+ link: "/data-structures",
+ flag: true
},
{
title: "Dynamic Programming",
@@ -51,11 +65,15 @@ const sections = [
"Step through state transitions and table updates to grasp DP intuitively.",
phase: "Phase 3",
img: "",
+ link: "/dynamic-programming",
+ flag: true
},
];
const Homepage = () => {
+ const navigate = useNavigate();
+
return (
{/* Full-Page Animated Gradient Background */}
@@ -93,6 +111,7 @@ const Homepage = () => {
{sections.map((section, index) => (
section.link && navigate(section.link)}
className="relative group bg-white/10 border border-white/10 rounded-2xl overflow-hidden shadow-2xl hover:shadow-indigo-500/40 transition-all duration-300 backdrop-blur-md hover:-translate-y-2"
>
{/* Image */}
@@ -119,9 +138,11 @@ const Homepage = () => {
{/* SaaS-style “Coming Soon” Overlay */}
-
- Coming Soon 🚀
-
+ {section.flag && (
+
+ Coming Soon 🚀
+
+)}
))}
diff --git a/src/pages/sorting/SelectionSort.jsx b/src/pages/sorting/SelectionSort.jsx
new file mode 100644
index 0000000..13a51d3
--- /dev/null
+++ b/src/pages/sorting/SelectionSort.jsx
@@ -0,0 +1,83 @@
+import React, { useState } from "react";
+import { Toaster } from "react-hot-toast";
+import SelectionSortVisualizer from "../../components/sorting/SelectionSortVisualizer";
+import { selectionSort } from "../../algorithms/sorting/selectionSort";
+
+export default function SelectionSort() {
+ const [array, setArray] = useState([]);
+ const [input, setInput] = useState("");
+ const [highlight, setHighlight] = useState(null);
+ const [isRunning, setIsRunning] = useState(false);
+
+ const handleStart = async () => {
+ if (isRunning || array.length === 0) return;
+ setIsRunning(true);
+
+ const gen = selectionSort(array);
+ for (let step of gen) {
+ setHighlight(step);
+ if (step.array) setArray([...step.array]);
+ await new Promise((r) => setTimeout(r, 500));
+ }
+
+ setHighlight({ type: "done" });
+ setIsRunning(false);
+ };
+
+ const handleReset = () => {
+ setArray([]);
+ setInput("");
+ setHighlight(null);
+ };
+
+ const handleInput = (e) => {
+ setInput(e.target.value);
+ const numbers = e.target.value
+ .split(",")
+ .map((n) => parseInt(n.trim()))
+ .filter((n) => !isNaN(n));
+ setArray(numbers);
+ };
+
+ return (
+
+
+
+ Selection Sort Visualizer
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/pages/sorting/SortingPage.jsx b/src/pages/sorting/SortingPage.jsx
new file mode 100644
index 0000000..e91d8c4
--- /dev/null
+++ b/src/pages/sorting/SortingPage.jsx
@@ -0,0 +1,63 @@
+// src/pages/SortingPage.jsx
+import React, { useState } from "react";
+import SelectionSort from "./SelectionSort";
+
+export default function SortingPage() {
+ const [selectedAlgo, setSelectedAlgo] = useState("");
+
+ const renderAlgorithm = () => {
+ switch (selectedAlgo) {
+ case "selection":
+ return ;
+ // You can add more later like:
+ // case "bubble": return ;
+ // case "merge": return ;
+ default:
+ return (
+
+ Select an algorithm to visualize 👆
+
+ );
+ }
+ };
+
+ return (
+
+ {/* Left Sidebar */}
+
+
+ Sorting Panel
+
+
+
+
+
+
+
+
+ ← Back to Home
+
+
+
+ {/* Right Visualization Area */}
+
+ {renderAlgorithm()}
+
+
+ );
+}