diff --git a/src/app/utils/NotFound.tsx b/src/app/utils/NotFound.tsx index 69b2315..ef9fe68 100644 --- a/src/app/utils/NotFound.tsx +++ b/src/app/utils/NotFound.tsx @@ -1,8 +1,17 @@ +import { CommitsGrid } from "@/components/commits-grid" +import { Button } from "@/components/ui/button"; +import { useNavigate } from "react-router"; + export default function NotFound() { + const navigate = useNavigate(); + return ( -
-

404 - Not Found

-

The page you are looking for does not exist.

+
+ +

The page you are looking for does not exist.

+
); -} \ No newline at end of file +} diff --git a/src/components/commits-grid.tsx b/src/components/commits-grid.tsx new file mode 100644 index 0000000..7d5d05f --- /dev/null +++ b/src/components/commits-grid.tsx @@ -0,0 +1,179 @@ +"use client" + +import * as React from "react" +import { cn } from "@/lib/utils"; +import type { CSSProperties } from "react"; + +export const CommitsGrid = ({ text }: { text: string }) => { + const cleanString = (str: string): string => { + const upperStr = str.toUpperCase(); + + const withoutAccents = upperStr + .normalize("NFD") + .replace(/[\u0300-\u036f]/g, ""); + + const allowedChars = Object.keys(letterPatterns); + return withoutAccents + .split("") + .filter((char) => allowedChars.includes(char)) + .join(""); + }; + + const generateHighlightedCells = (text: string) => { + const cleanedText = cleanString(text); + + const width = Math.max(cleanedText.length * 6, 6) + 1; + + let currentPosition = 1; // we start at 1 to leave space for the top border + const highlightedCells: number[] = []; + + cleanedText + .toUpperCase() + .split("") + .forEach((char) => { + if (letterPatterns[char]) { + const pattern = letterPatterns[char].map((pos) => { + const row = Math.floor(pos / 50); + const col = pos % 50; + return (row + 1) * width + col + currentPosition; + }); + highlightedCells.push(...pattern); + } + currentPosition += 6; + }); + + return { + cells: highlightedCells, + width, + height: 9, // 7+2 for the top and bottom borders + }; + }; + + const { + cells: highlightedCells, + width: gridWidth, + height: gridHeight, + } = generateHighlightedCells(text); + + const getRandomColor = () => { + const commitColors = [ + "#48d55d", + "#016d32", + "#0d4429" + ]; + const randomIndex = Math.floor(Math.random() * commitColors.length); + return commitColors[randomIndex]; + }; + + const getRandomDelay = () => `${(Math.random() * 0.6).toFixed(1)}s`; + const getRandomFlash = () => +(Math.random() < 0.3); + + return ( +
+ {Array.from({ length: gridWidth * gridHeight }).map((_, index) => { + const isHighlighted = highlightedCells.includes(index); + const shouldFlash = !isHighlighted && getRandomFlash(); + + return ( +
+ ); + })} +
+ ); +}; + +const letterPatterns: { [key: string]: number[] } = { + A: [ + 1, 2, 3, 50, 100, 150, 200, 250, 300, 54, 104, 154, 204, 254, 304, 151, 152, + 153, + ], + B: [ + 0, 1, 2, 3, 4, 50, 100, 150, 151, 200, 250, 300, 301, 302, 303, 304, 54, + 104, 152, 153, 204, 254, 303, + ], + C: [0, 1, 2, 3, 4, 50, 100, 150, 200, 250, 300, 301, 302, 303, 304], + D: [ + 0, 1, 2, 3, 50, 100, 150, 200, 250, 300, 301, 302, 54, 104, 154, 204, 254, + 303, + ], + E: [0, 1, 2, 3, 4, 50, 100, 150, 200, 250, 300, 301, 302, 303, 304, 151, 152], + F: [0, 1, 2, 3, 4, 50, 100, 150, 200, 250, 300, 151, 152, 153], + G: [ + 0, 1, 2, 3, 4, 50, 100, 150, 200, 250, 300, 301, 302, 303, 153, 204, 154, + 304, 254, + ], + H: [ + 0, 50, 100, 150, 200, 250, 300, 151, 152, 153, 4, 54, 104, 154, 204, 254, + 304, + ], + I: [0, 1, 2, 3, 4, 52, 102, 152, 202, 252, 300, 301, 302, 303, 304], + J: [0, 1, 2, 3, 4, 52, 102, 152, 202, 250, 252, 302, 300, 301], + K: [0, 4, 50, 100, 150, 200, 250, 300, 151, 152, 103, 54, 203, 254, 304], + L: [0, 50, 100, 150, 200, 250, 300, 301, 302, 303, 304], + M: [ + 0, 50, 100, 150, 200, 250, 300, 51, 102, 53, 4, 54, 104, 154, 204, 254, 304, + ], + N: [ + 0, 50, 100, 150, 200, 250, 300, 51, 102, 153, 204, 4, 54, 104, 154, 204, + 254, 304, + ], + Ñ: [ + 0, 50, 100, 150, 200, 250, 300, 51, 102, 153, 204, 4, 54, 104, 154, 204, + 254, 304, + ], + O: [1, 2, 3, 50, 100, 150, 200, 250, 301, 302, 303, 54, 104, 154, 204, 254], + P: [0, 50, 100, 150, 200, 250, 300, 1, 2, 3, 54, 104, 151, 152, 153], + Q: [ + 1, 2, 3, 50, 100, 150, 200, 250, 301, 302, 54, 104, 154, 204, 202, 253, 304, + ], + R: [ + 0, 50, 100, 150, 200, 250, 300, 1, 2, 3, 54, 104, 151, 152, 153, 204, 254, + 304, + ], + S: [1, 2, 3, 4, 50, 100, 151, 152, 153, 204, 254, 300, 301, 302, 303], + T: [0, 1, 2, 3, 4, 52, 102, 152, 202, 252, 302], + U: [0, 50, 100, 150, 200, 250, 301, 302, 303, 4, 54, 104, 154, 204, 254], + V: [0, 50, 100, 150, 200, 251, 302, 4, 54, 104, 154, 204, 253], + W: [ + 0, 50, 100, 150, 200, 250, 301, 152, 202, 252, 4, 54, 104, 154, 204, 254, + 303, + ], + X: [0, 50, 203, 254, 304, 4, 54, 152, 101, 103, 201, 250, 300], + Y: [0, 50, 101, 152, 202, 252, 302, 4, 54, 103], + Z: [0, 1, 2, 3, 4, 54, 103, 152, 201, 250, 300, 301, 302, 303, 304], + "0": [1, 2, 3, 50, 100, 150, 200, 250, 301, 302, 303, 54, 104, 154, 204, 254], + "1": [1, 52, 102, 152, 202, 252, 302, 0, 2, 300, 301, 302, 303, 304], + "2": [0, 1, 2, 3, 54, 104, 152, 153, 201, 250, 300, 301, 302, 303, 304], + "3": [0, 1, 2, 3, 54, 104, 152, 153, 204, 254, 300, 301, 302, 303], + "4": [0, 50, 100, 150, 4, 54, 104, 151, 152, 153, 154, 204, 254, 304], + "5": [0, 1, 2, 3, 4, 50, 100, 151, 152, 153, 204, 254, 300, 301, 302, 303], + "6": [ + 1, 2, 3, 50, 100, 150, 151, 152, 153, 200, 250, 301, 302, 204, 254, 303, + ], + "7": [0, 1, 2, 3, 4, 54, 103, 152, 201, 250, 300], + "8": [ + 1, 2, 3, 50, 100, 151, 152, 153, 200, 250, 301, 302, 303, 54, 104, 204, 254, + ], + "9": [1, 2, 3, 50, 100, 151, 152, 153, 154, 204, 254, 304, 54, 104], + " ": [], +}; diff --git a/styles/globals.css b/styles/globals.css index dd34342..b4ebfe9 100644 --- a/styles/globals.css +++ b/styles/globals.css @@ -142,3 +142,29 @@ @apply bg-background text-foreground; } } + +@theme { + --animate-highlight: highlight 0.6s ease forwards; + --animate-flash: flash 0.6s ease forwards; + + @keyframes highlight { + 0% { + background-color: transparent; + } + 100% { + background-color: var(--highlight); + } + } + + @keyframes flash { + 0% { + background-color: hsl(var(--card)); + } + 50% { + background-color: var(--highlight); + } + 100% { + background-color: hsl(var(--card)); + } + } +} \ No newline at end of file