├── .eslintrc.json
├── app
├── page.module.css
├── favicon.ico
├── globals.css
├── apply
│ └── page.tsx
├── resume
│ └── page.tsx
├── sitemap.ts
├── project
│ ├── [name]
│ │ ├── loading.tsx
│ │ └── page.tsx
│ └── page.tsx
├── layout.tsx
├── hackathons
│ └── page.tsx
└── page.tsx
├── bun.lockb
├── public
├── lkps.png
├── logo.png
├── og.png
├── pfp.jpeg
├── annu.webp
├── glanza.png
├── oneko.gif
├── cattype.webp
├── citronics.png
├── doblar.webp
├── elafda.webp
├── favicon.ico
├── invoicely.png
├── linkedin.png
├── nexusread.png
├── aurastake.webp
├── getaresume.png
├── lkpsresult.png
├── nexusevents.png
├── techkareer.jpeg
├── AniruddhResume.pdf
├── autodiagram.webp
├── buildaresume.png
├── nexusdashboard.png
├── robots.txt
├── hackathons
│ ├── image1.webp
│ ├── image10.webp
│ ├── image11.webp
│ ├── image12.webp
│ ├── image13.webp
│ ├── image14.webp
│ ├── image16.webp
│ ├── image17.webp
│ ├── image18.webp
│ ├── image19.webp
│ ├── image2.webp
│ ├── image20.webp
│ ├── image21.webp
│ ├── image3.webp
│ ├── image4.webp
│ ├── image5.webp
│ ├── image6.webp
│ ├── image7.webp
│ ├── image8.webp
│ └── image9.webp
├── vercel.svg
├── next.svg
├── merlin.svg
├── oneko.js
└── leapflow.svg
├── utils
└── uppercase.ts
├── postcss.config.js
├── components
├── svgs
│ ├── index.tsx
│ ├── twitter.tsx
│ ├── mail.tsx
│ ├── github.tsx
│ └── linkedin.tsx
├── badge.tsx
├── shared
│ └── back.tsx
├── loadingimage.tsx
├── transition.tsx
├── modal.tsx
├── workexp.tsx
├── text.tsx
├── Masonry
│ └── Masonry.tsx
├── hackathons.tsx
└── links.tsx
├── lib
└── utils.ts
├── next.config.js
├── tailwind.config.js
├── .vscode
└── settings.json
├── .gitignore
├── tsconfig.json
├── package.json
├── README.md
└── constants
├── hackathons.ts
└── index.ts
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "next/core-web-vitals"
3 | }
4 |
--------------------------------------------------------------------------------
/app/page.module.css:
--------------------------------------------------------------------------------
1 | .article:hover > svg {
2 | @apply text-white;
3 | }
4 |
--------------------------------------------------------------------------------
/bun.lockb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icantcodefyi/portfolio/HEAD/bun.lockb
--------------------------------------------------------------------------------
/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icantcodefyi/portfolio/HEAD/app/favicon.ico
--------------------------------------------------------------------------------
/public/lkps.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icantcodefyi/portfolio/HEAD/public/lkps.png
--------------------------------------------------------------------------------
/public/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icantcodefyi/portfolio/HEAD/public/logo.png
--------------------------------------------------------------------------------
/public/og.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icantcodefyi/portfolio/HEAD/public/og.png
--------------------------------------------------------------------------------
/public/pfp.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icantcodefyi/portfolio/HEAD/public/pfp.jpeg
--------------------------------------------------------------------------------
/public/annu.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icantcodefyi/portfolio/HEAD/public/annu.webp
--------------------------------------------------------------------------------
/public/glanza.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icantcodefyi/portfolio/HEAD/public/glanza.png
--------------------------------------------------------------------------------
/public/oneko.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icantcodefyi/portfolio/HEAD/public/oneko.gif
--------------------------------------------------------------------------------
/public/cattype.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icantcodefyi/portfolio/HEAD/public/cattype.webp
--------------------------------------------------------------------------------
/public/citronics.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icantcodefyi/portfolio/HEAD/public/citronics.png
--------------------------------------------------------------------------------
/public/doblar.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icantcodefyi/portfolio/HEAD/public/doblar.webp
--------------------------------------------------------------------------------
/public/elafda.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icantcodefyi/portfolio/HEAD/public/elafda.webp
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icantcodefyi/portfolio/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/public/invoicely.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icantcodefyi/portfolio/HEAD/public/invoicely.png
--------------------------------------------------------------------------------
/public/linkedin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icantcodefyi/portfolio/HEAD/public/linkedin.png
--------------------------------------------------------------------------------
/public/nexusread.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icantcodefyi/portfolio/HEAD/public/nexusread.png
--------------------------------------------------------------------------------
/public/aurastake.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icantcodefyi/portfolio/HEAD/public/aurastake.webp
--------------------------------------------------------------------------------
/public/getaresume.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icantcodefyi/portfolio/HEAD/public/getaresume.png
--------------------------------------------------------------------------------
/public/lkpsresult.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icantcodefyi/portfolio/HEAD/public/lkpsresult.png
--------------------------------------------------------------------------------
/public/nexusevents.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icantcodefyi/portfolio/HEAD/public/nexusevents.png
--------------------------------------------------------------------------------
/public/techkareer.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icantcodefyi/portfolio/HEAD/public/techkareer.jpeg
--------------------------------------------------------------------------------
/public/AniruddhResume.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icantcodefyi/portfolio/HEAD/public/AniruddhResume.pdf
--------------------------------------------------------------------------------
/public/autodiagram.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icantcodefyi/portfolio/HEAD/public/autodiagram.webp
--------------------------------------------------------------------------------
/public/buildaresume.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icantcodefyi/portfolio/HEAD/public/buildaresume.png
--------------------------------------------------------------------------------
/public/nexusdashboard.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icantcodefyi/portfolio/HEAD/public/nexusdashboard.png
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | User-agent: *
2 | Allow: /
3 |
4 | # Sitemap file
5 | Sitemap: https://ani.ink/sitemap.xml
6 |
--------------------------------------------------------------------------------
/public/hackathons/image1.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icantcodefyi/portfolio/HEAD/public/hackathons/image1.webp
--------------------------------------------------------------------------------
/public/hackathons/image10.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icantcodefyi/portfolio/HEAD/public/hackathons/image10.webp
--------------------------------------------------------------------------------
/public/hackathons/image11.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icantcodefyi/portfolio/HEAD/public/hackathons/image11.webp
--------------------------------------------------------------------------------
/public/hackathons/image12.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icantcodefyi/portfolio/HEAD/public/hackathons/image12.webp
--------------------------------------------------------------------------------
/public/hackathons/image13.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icantcodefyi/portfolio/HEAD/public/hackathons/image13.webp
--------------------------------------------------------------------------------
/public/hackathons/image14.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icantcodefyi/portfolio/HEAD/public/hackathons/image14.webp
--------------------------------------------------------------------------------
/public/hackathons/image16.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icantcodefyi/portfolio/HEAD/public/hackathons/image16.webp
--------------------------------------------------------------------------------
/public/hackathons/image17.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icantcodefyi/portfolio/HEAD/public/hackathons/image17.webp
--------------------------------------------------------------------------------
/public/hackathons/image18.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icantcodefyi/portfolio/HEAD/public/hackathons/image18.webp
--------------------------------------------------------------------------------
/public/hackathons/image19.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icantcodefyi/portfolio/HEAD/public/hackathons/image19.webp
--------------------------------------------------------------------------------
/public/hackathons/image2.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icantcodefyi/portfolio/HEAD/public/hackathons/image2.webp
--------------------------------------------------------------------------------
/public/hackathons/image20.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icantcodefyi/portfolio/HEAD/public/hackathons/image20.webp
--------------------------------------------------------------------------------
/public/hackathons/image21.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icantcodefyi/portfolio/HEAD/public/hackathons/image21.webp
--------------------------------------------------------------------------------
/public/hackathons/image3.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icantcodefyi/portfolio/HEAD/public/hackathons/image3.webp
--------------------------------------------------------------------------------
/public/hackathons/image4.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icantcodefyi/portfolio/HEAD/public/hackathons/image4.webp
--------------------------------------------------------------------------------
/public/hackathons/image5.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icantcodefyi/portfolio/HEAD/public/hackathons/image5.webp
--------------------------------------------------------------------------------
/public/hackathons/image6.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icantcodefyi/portfolio/HEAD/public/hackathons/image6.webp
--------------------------------------------------------------------------------
/public/hackathons/image7.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icantcodefyi/portfolio/HEAD/public/hackathons/image7.webp
--------------------------------------------------------------------------------
/public/hackathons/image8.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icantcodefyi/portfolio/HEAD/public/hackathons/image8.webp
--------------------------------------------------------------------------------
/public/hackathons/image9.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icantcodefyi/portfolio/HEAD/public/hackathons/image9.webp
--------------------------------------------------------------------------------
/utils/uppercase.ts:
--------------------------------------------------------------------------------
1 | export const upperFirst = (str: string) =>
2 | str.charAt(0).toUpperCase() + str.slice(1);
3 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/components/svgs/index.tsx:
--------------------------------------------------------------------------------
1 | export * from './mail';
2 | export * from './github';
3 | export * from './twitter';
4 | export * from './linkedin';
--------------------------------------------------------------------------------
/lib/utils.ts:
--------------------------------------------------------------------------------
1 | import { type ClassValue, clsx } from 'clsx';
2 | import { twMerge } from 'tailwind-merge';
3 |
4 | export function cn(...inputs: ClassValue[]) {
5 | return twMerge(clsx(inputs));
6 | }
--------------------------------------------------------------------------------
/app/globals.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | body {
6 | @apply bg-[#111010] text-white;
7 | }
8 |
9 | .image__container img {
10 | position: unset !important;
11 | }
12 |
--------------------------------------------------------------------------------
/next.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | const nextConfig = {
3 | images: {
4 | domains: [
5 | "avatars.githubusercontent.com",
6 | "repository-images.githubusercontent.com",
7 | "github.com",
8 | "cdn2.thecatapi.com",
9 |
10 | ],
11 | },
12 | swcMinify: true,
13 | };
14 |
15 | module.exports = nextConfig;
16 |
--------------------------------------------------------------------------------
/components/badge.tsx:
--------------------------------------------------------------------------------
1 | import { ComponentProps } from "react";
2 |
3 | export default function Badge(props: ComponentProps<"a">) {
4 | return (
5 |
10 | );
11 | }
12 |
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | module.exports = {
3 | content: [
4 | './pages/**/*.{js,ts,jsx,tsx,mdx}',
5 | './components/**/*.{js,ts,jsx,tsx,mdx}',
6 | './app/**/*.{js,ts,jsx,tsx,mdx}',
7 | ],
8 | theme: {
9 | extend: {
10 | backgroundImage: {
11 | 'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))',
12 | 'gradient-conic':
13 | 'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))',
14 | },
15 | },
16 | },
17 | plugins: [],
18 | }
19 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "typescript.tsdk": "node_modules\\.pnpm\\typescript@5.0.4\\node_modules\\typescript\\lib",
3 | "typescript.enablePromptUseWorkspaceTsdk": true,
4 | "[markdown]": {
5 | "editor.fontSize": 14,
6 | "editor.lineHeight": 26,
7 | "editor.wordWrap": "wordWrapColumn",
8 | "editor.wordWrapColumn": 64,
9 | "editor.lineNumbers": "off",
10 | "editor.quickSuggestions": {
11 | "comments": "off",
12 | "strings": "off",
13 | "other": "off"
14 | },
15 | "editor.minimap.enabled": false
16 | }
17 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # next.js
12 | /.next/
13 | /out/
14 |
15 | # production
16 | /build
17 |
18 | # misc
19 | .DS_Store
20 | *.pem
21 |
22 | # debug
23 | npm-debug.log*
24 | yarn-debug.log*
25 | yarn-error.log*
26 |
27 | # local env files
28 | .env*.local
29 |
30 | # vercel
31 | .vercel
32 |
33 | # typescript
34 | *.tsbuildinfo
35 | next-env.d.ts
36 |
37 | .contentlayer
38 | .frontmatter
--------------------------------------------------------------------------------
/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/components/shared/back.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { useRouter } from "next/navigation";
4 | import { BiArrowBack } from "react-icons/bi";
5 |
6 | export default function BackButton() {
7 | const { back } = useRouter();
8 | return (
9 |
18 |
19 |
20 | );
21 | }
22 |
--------------------------------------------------------------------------------
/app/apply/page.tsx:
--------------------------------------------------------------------------------
1 | export default function FocusPage() {
2 | return (
3 |
12 |
Focus on what matters
13 |
You should study and focus on your dev and DSA skills.
14 |
Stop using Twitter and getting baited for some silly engagement.
15 |
16 | )
17 | }
18 |
19 |
--------------------------------------------------------------------------------
/app/resume/page.tsx:
--------------------------------------------------------------------------------
1 | import BackButton from "@/components/shared/back";
2 |
3 | export default function Resume() {
4 | return (
5 |
6 |
7 |
8 |
My Resume
9 |
18 |
19 |
20 | );
21 | }
22 |
--------------------------------------------------------------------------------
/components/loadingimage.tsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable jsx-a11y/alt-text */
2 | "use client";
3 |
4 | import { cn } from "@/lib/utils";
5 | import Image from "next/image";
6 | import { ComponentProps, useState } from "react";
7 |
8 | export default function ImageWithLoader({
9 | onLoad,
10 | className,
11 | ...rest
12 | }: ComponentProps) {
13 | const [loading, setLoading] = useState(true);
14 | return (
15 | setLoading(false)}
25 | />
26 | );
27 | }
28 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": ["dom", "dom.iterable", "esnext"],
5 | "allowJs": true,
6 | "skipLibCheck": true,
7 | "strict": true,
8 | "forceConsistentCasingInFileNames": true,
9 | "noEmit": true,
10 | "esModuleInterop": true,
11 | "module": "esnext",
12 | "moduleResolution": "node",
13 | "resolveJsonModule": true,
14 | "isolatedModules": true,
15 | "jsx": "preserve",
16 | "incremental": true,
17 | "plugins": [
18 | {
19 | "name": "next"
20 | }
21 | ],
22 | "paths": {
23 | "@/*": ["./*"]
24 | }
25 | },
26 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
27 | "exclude": ["node_modules"]
28 | }
29 |
--------------------------------------------------------------------------------
/components/transition.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { useEffect, useState } from "react";
4 | import TextTransition from "./text";
5 | import { config } from "@react-spring/web";
6 | const TEXTS = ["website", "backend", "app"];
7 |
8 | export default function Transition() {
9 | const [index, setIndex] = useState(0);
10 |
11 | useEffect(() => {
12 | const intervalId = setInterval(
13 | () => setIndex((index) => index + 1),
14 | 3000 // every 3 seconds
15 | );
16 | return () => clearTimeout(intervalId);
17 | }, []);
18 |
19 | return (
20 |
21 |
22 | {TEXTS[index % TEXTS.length]}
23 |
24 | s.
25 |
26 | );
27 | }
28 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "portfolio",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "@openpanel/nextjs": "^1.0.7",
13 | "@react-spring/web": "^9.7.4",
14 | "@types/node": "20.11.20",
15 | "@types/react": "18.2.58",
16 | "@types/react-dom": "18.2.19",
17 | "autoprefix": "^1.0.1",
18 | "autoprefixer": "10.4.15",
19 | "clsx": "^2.1.0",
20 | "eslint": "8.57.0",
21 | "eslint-config-next": "14.1.0",
22 | "motion": "^12.17.0",
23 | "next": "14.1.0",
24 | "postcss": "8.4.35",
25 | "react": "18.2.0",
26 | "react-dom": "18.2.0",
27 | "react-icons": "^5.0.1",
28 | "tailwind-merge": "^2.2.1",
29 | "tailwindcss": "3.4.1",
30 | "typescript": "5.3.3"
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/app/sitemap.ts:
--------------------------------------------------------------------------------
1 | import { MetadataRoute } from 'next'
2 | import { Projects } from '@/constants'
3 |
4 | export default function sitemap(): MetadataRoute.Sitemap {
5 | const baseUrl = 'https://ani.ink'
6 |
7 | // Base routes
8 | const routes = [
9 | {
10 | url: baseUrl,
11 | lastModified: new Date(),
12 | changeFrequency: 'monthly' as const,
13 | priority: 1,
14 | },
15 | {
16 | url: `${baseUrl}/project`,
17 | lastModified: new Date(),
18 | changeFrequency: 'monthly' as const,
19 | priority: 0.8,
20 | },
21 | {
22 | url: `${baseUrl}/resume`,
23 | lastModified: new Date(),
24 | changeFrequency: 'monthly' as const,
25 | priority: 0.8,
26 | },
27 | {
28 | url: `${baseUrl}/hackathons`,
29 | lastModified: new Date(),
30 | changeFrequency: 'monthly' as const,
31 | priority: 0.8,
32 | },
33 | ]
34 |
35 | // Add individual project routes
36 | const projectRoutes = Projects.map((project) => ({
37 | url: `${baseUrl}/project/${project.slug}`,
38 | lastModified: new Date(),
39 | changeFrequency: 'monthly' as const,
40 | priority: 0.6,
41 | }))
42 |
43 | return [...routes, ...projectRoutes]
44 | }
--------------------------------------------------------------------------------
/public/next.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/merlin.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/components/modal.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 | import { useCallback, useRef, useEffect, ReactNode, MouseEvent } from "react";
3 | import { useRouter } from "next/navigation";
4 |
5 | export default function Modal({ children }: { children: ReactNode }) {
6 | const overlay = useRef(null);
7 | const wrapper = useRef(null);
8 | const router = useRouter();
9 |
10 | const onDismiss = useCallback(() => {
11 | router.back();
12 | }, [router]);
13 |
14 | const onClick = useCallback(
15 | (e: any) => {
16 | if (e.target === overlay.current || e.target === wrapper.current) {
17 | if (onDismiss) onDismiss();
18 | }
19 | },
20 | [onDismiss, overlay, wrapper]
21 | );
22 |
23 | const onKeyDown = useCallback(
24 | (e: any) => {
25 | if (e.key === "Escape") onDismiss();
26 | },
27 | [onDismiss]
28 | );
29 |
30 | useEffect(() => {
31 | document.addEventListener("keydown", onKeyDown);
32 | return () => document.removeEventListener("keydown", onKeyDown);
33 | }, [onKeyDown]);
34 |
35 | return (
36 |
41 |
45 | {children}
46 |
47 |
48 | );
49 | }
50 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
2 |
3 | ## Getting Started
4 | e
5 | First, run the development server:
6 |
7 | ```bash
8 | npm run dev
9 | # or
10 | yarn dev
11 | # orw
12 | pnpm dev
13 | ```
14 |
15 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
16 |
17 | You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
18 |
19 | [http://localhost:3000/api/hello](http://localhost:3000/api/hello) is an endpoint that uses [Route Handlers](https://beta.nextjs.org/docs/routing/route-handlers). This endpoint can be edited in `app/api/hello/route.ts`.
20 |
21 | This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
22 |
23 | ## Learn More
24 |
25 | To learn more about Next.js, take a look at the following resources:
26 |
27 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
28 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
29 |
30 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
31 |
32 | ## Deploy on Vercel
33 |
34 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
35 |
36 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
37 |
--------------------------------------------------------------------------------
/app/project/[name]/loading.tsx:
--------------------------------------------------------------------------------
1 | export default function Loading() {
2 | return (
3 |
6 |
7 |
14 |
18 |
22 |
23 |
Loading...
24 |
25 |
26 | );
27 | }
28 |
--------------------------------------------------------------------------------
/app/project/page.tsx:
--------------------------------------------------------------------------------
1 | import Link from "next/link";
2 | import { AiOutlineRight } from "react-icons/ai";
3 | import { Projects } from "@/constants";
4 | import BackButton from "@/components/shared/back";
5 |
6 | export default function Project() {
7 | return (
8 |
9 |
10 |
11 |
12 |
Projects
13 |
14 | {Projects.map((project) => (
15 |
20 |
21 |
22 |
23 | {project.name}
24 |
25 |
26 |
27 |
31 |
32 |
33 | ))}
34 |
35 |
36 |
37 |
38 | );
39 | }
40 |
--------------------------------------------------------------------------------
/app/layout.tsx:
--------------------------------------------------------------------------------
1 | import "./globals.css";
2 | import { Hanken_Grotesk } from "next/font/google";
3 | const font = Hanken_Grotesk({ subsets: ["latin"] });
4 | import { OpenPanelComponent } from '@openpanel/nextjs';
5 | import Script from "next/script";
6 | import { Metadata } from "next";
7 |
8 | export const metadata: Metadata = {
9 | metadataBase: new URL("https://ani.ink"),
10 | title: {
11 | default: "Aniruddh",
12 | template: "%s | Aniruddh",
13 | },
14 | description: "Full Stack Web Developer and Designer from India, specializing in Next.js, React, and TypeScript.",
15 | keywords: ["Full Stack Developer", "Web Designer", "Next.js", "React", "TypeScript", "India"],
16 | authors: [{ name: "Aniruddh", url: "https://ani.ink/" }],
17 | creator: "Aniruddh",
18 | icons: {
19 | icon: [
20 | { url: '/favicon.ico', sizes: 'any' },
21 | { url: '/icon.png', type: 'image/png' }
22 | ],
23 | shortcut: '/favicon.ico',
24 | apple: '/apple-icon.png',
25 | },
26 | openGraph: {
27 | type: "website",
28 | locale: "en_US",
29 | url: "https://ani.ink",
30 | siteName: "Aniruddh's Portfolio",
31 | title: "Aniruddh - Full Stack Developer",
32 | description: "Full Stack Web Developer and Designer from India, specializing in Next.js, React, and TypeScript.",
33 | images: [
34 | {
35 | url: "/og.png",
36 | width: 1200,
37 | height: 675,
38 | alt: "Aniruddh's Profile Picture",
39 | },
40 | ],
41 | },
42 | twitter: {
43 | card: "summary_large_image",
44 | site: "@icantcodefyi",
45 | creator: "@icantcodefyi",
46 | images: [
47 | {
48 | url: "/og.png",
49 | width: 1200,
50 | height: 675,
51 | alt: "Aniruddh's Profile Picture",
52 | },
53 | ],
54 | },
55 | };
56 |
57 | export default function RootLayout({
58 | children,
59 | }: {
60 | children: React.ReactNode;
61 | }) {
62 | return (
63 |
64 |
65 |
69 | {children}
70 |
71 |
72 |
73 | );
74 | }
--------------------------------------------------------------------------------
/components/workexp.tsx:
--------------------------------------------------------------------------------
1 | import Link from "next/link";
2 | import { JetBrains_Mono } from "next/font/google";
3 |
4 | const jetbrainsMono = JetBrains_Mono({ subsets: ["latin"] });
5 |
6 | interface WorkExperience {
7 | company: string;
8 | position: string;
9 | duration: string;
10 | location: string;
11 | link: string;
12 | image: string;
13 | }
14 |
15 | const experiences: WorkExperience[] = [
16 | {
17 | company: "Leapflow",
18 | position: "Full Stack Developer ",
19 | location: "Remote",
20 | duration: "Aug 2024 – Present",
21 | link: "https://leapflow.tech",
22 | image: "/leapflow.svg",
23 | },
24 | {
25 | company: "Merlin AI",
26 | position: "Software Engineer Intern",
27 | location: "Bengaluru, India",
28 | duration: "Sept 2024 – Feb 2025",
29 | link: "https://getmerlin.in",
30 | image: "/merlin.svg",
31 | },
32 | {
33 | company: "TechKareer",
34 | position: "Software Engineer Intern",
35 | location: "Remote",
36 | duration: "Jun 2024 – Aug 2024",
37 | link: "https://techkareer.com",
38 | image: "/techkareer.jpeg",
39 | },
40 | ];
41 |
42 | export default function WorkExperience() {
43 | return (
44 |
45 |
46 | {experiences.map((exp) => (
47 |
53 |
54 |
55 |
56 |
57 |
58 | {exp.company}
59 |
60 |
{exp.location}
61 |
62 |
63 |
{exp.position}
64 |
{exp.duration}
65 |
66 |
67 |
68 |
69 | ))}
70 |
71 |
72 | );
73 | }
74 |
--------------------------------------------------------------------------------
/components/text.tsx:
--------------------------------------------------------------------------------
1 | // taken from https://github.com/WinterCore/react-text-transition/blob/master/src/components/TextTransition.tsx
2 | import React, { useState, useRef, useEffect } from "react";
3 | import type { CSSProperties, PropsWithChildren } from "react";
4 |
5 | import {
6 | useSpring,
7 | useTransition,
8 | animated,
9 | config,
10 | SpringConfig,
11 | } from "@react-spring/web";
12 |
13 | export interface TextTransitionProps {
14 | className?: string;
15 | delay?: number;
16 | direction?: "up" | "down";
17 | inline?: boolean;
18 | springConfig?: SpringConfig;
19 | style?: CSSProperties;
20 | translateValue?: string;
21 | }
22 |
23 | function TextTransition(props: PropsWithChildren) {
24 | const {
25 | direction = "up",
26 | inline = false,
27 | springConfig = config.default,
28 | delay = 0,
29 | className,
30 | style,
31 | translateValue: tv = "100%",
32 | children,
33 | } = props;
34 |
35 | const initialRun = useRef(true);
36 | const fromTransform = direction === "down" ? `-${tv}` : tv;
37 | const leaveTransform = direction === "down" ? tv : `-${tv}`;
38 |
39 | const transitions = useTransition([children], {
40 | enter: { opacity: 1, transform: "translateY(0%)" },
41 | from: { opacity: 0, transform: `translateY(${fromTransform})` },
42 | leave: {
43 | opacity: 0,
44 | transform: `translateY(${leaveTransform})`,
45 | position: "absolute",
46 | },
47 | config: springConfig,
48 | immediate: initialRun.current,
49 | delay: !initialRun.current ? delay : undefined,
50 | });
51 |
52 | const [width, setWidth] = useState(0);
53 | const currentRef = useRef(null);
54 | const heightRef = useRef("auto");
55 |
56 | useEffect(() => {
57 | initialRun.current = false;
58 | const element = currentRef.current;
59 |
60 | // If element doesn't exist, then do nothing
61 | if (!element) return;
62 |
63 | const { width, height } = element.getBoundingClientRect();
64 |
65 | setWidth(width);
66 | heightRef.current = height;
67 | }, [children, setWidth, currentRef]);
68 |
69 | const widthTransition = useSpring({
70 | to: { width },
71 | config: springConfig,
72 | immediate: initialRun.current,
73 | delay: !initialRun.current ? delay : undefined,
74 | });
75 |
76 | return (
77 |
87 | {transitions((styles, item) => (
88 |
92 | {item}
93 |
94 | ))}
95 |
96 | );
97 | }
98 |
99 | export default TextTransition;
100 |
--------------------------------------------------------------------------------
/components/svgs/twitter.tsx:
--------------------------------------------------------------------------------
1 | 'use client';
2 |
3 | import { motion, useAnimation } from 'motion/react';
4 | import type { Variants } from 'motion/react';
5 | import type { HTMLAttributes } from 'react';
6 | import { forwardRef, useCallback, useImperativeHandle, useRef } from 'react';
7 | import { cn } from '@/lib/utils';
8 |
9 | export interface TwitterIconHandle {
10 | startAnimation: () => void;
11 | stopAnimation: () => void;
12 | }
13 |
14 | interface TwitterIconProps extends HTMLAttributes {
15 | size?: number;
16 | }
17 |
18 | const pathVariants: Variants = {
19 | normal: {
20 | opacity: 1,
21 | pathLength: 1,
22 | pathOffset: 0,
23 | transition: {
24 | duration: 0.4,
25 | opacity: { duration: 0.1 },
26 | },
27 | },
28 | animate: {
29 | opacity: [0, 1],
30 | pathLength: [0, 1],
31 | pathOffset: [1, 0],
32 | transition: {
33 | duration: 0.6,
34 | ease: 'linear',
35 | opacity: { duration: 0.1 },
36 | },
37 | },
38 | };
39 |
40 | const TwitterIcon = forwardRef(
41 | ({ onMouseEnter, onMouseLeave, className, size = 28, ...props }, ref) => {
42 | const controls = useAnimation();
43 | const isControlledRef = useRef(false);
44 |
45 | useImperativeHandle(ref, () => {
46 | isControlledRef.current = true;
47 |
48 | return {
49 | startAnimation: () => controls.start('animate'),
50 | stopAnimation: () => controls.start('normal'),
51 | };
52 | });
53 |
54 | const handleMouseEnter = useCallback(
55 | (e: React.MouseEvent) => {
56 | if (!isControlledRef.current) {
57 | controls.start('animate');
58 | } else {
59 | onMouseEnter?.(e);
60 | }
61 | },
62 | [controls, onMouseEnter]
63 | );
64 |
65 | const handleMouseLeave = useCallback(
66 | (e: React.MouseEvent) => {
67 | if (!isControlledRef.current) {
68 | controls.start('normal');
69 | } else {
70 | onMouseLeave?.(e);
71 | }
72 | },
73 | [controls, onMouseLeave]
74 | );
75 |
76 | return (
77 |
83 |
94 |
100 |
101 |
102 | );
103 | }
104 | );
105 |
106 | TwitterIcon.displayName = 'TwitterIcon';
107 |
108 | export { TwitterIcon };
109 |
--------------------------------------------------------------------------------
/constants/hackathons.ts:
--------------------------------------------------------------------------------
1 | export const hackathonWins = [
2 | {
3 | name: "Hackistica '23",
4 | venue: "IIT Indore",
5 | description:
6 | "My first hackathon and first win. Made a leetcode chrome extension here.",
7 | },
8 | {
9 | name: "EthIndia 2023",
10 | venue: "KTPO Bangalore",
11 | description: "My first flight to Blr, won multiple sponsor tracks here.",
12 | },
13 | {
14 | name: "Version Beta",
15 | venue: "MANIT Bhopal",
16 | description:
17 | "Got 3rd place for making an blockchain based financial companion.",
18 | },
19 | {
20 | name: "Syntax Error X",
21 | venue: "IIT Roorkee",
22 | description:
23 | "Won manga maestro track, made a platform to submit your mangas and ai which tags them as nsfw or other tags.",
24 | },
25 | {
26 | name: "Genesis 1.0",
27 | venue: "SRM Chennai",
28 | description: "Won berachain track for making a web3 native game.",
29 | },
30 | {
31 | name: "Udaymitsav '24",
32 | venue: "IIT Jammu",
33 | description:
34 | "Won 1st in my track and overall 2nd prize for making a micro blogging solution.",
35 | },
36 | {
37 | name: "HackHive",
38 | venue: "DAVV Indore",
39 | description:
40 | "Won 1st prize for making an ai agent which let you interact with websites.",
41 | },
42 | {
43 | name: "HackVSIT 5.0",
44 | venue: "VSIT Delhi",
45 | description:
46 | "Won 3rd prize for making an csv analyser which generates dashboard based off the data.",
47 | },
48 | {
49 | name: "Hakxite",
50 | description:
51 | "Won 1st prize for an ai based solution for deaf and dumb people.",
52 | },
53 | {
54 | name: "WebGranth",
55 | venue: "CDGI Indore",
56 | description:
57 | "Won 1st prize for making a all in one platform for learning new frameworks, technologies.",
58 | },
59 | {
60 | name: "Unfold 2024",
61 | venue: "Marriot Bangalore",
62 | description:
63 | "Won nethermind sponsor track for making an autonomous ai agent which can interact with blockchain and deploy its own smart contracts",
64 | },
65 | {
66 | name: "EthIndia 2024",
67 | venue: "KTPO Bangalore",
68 | description:
69 | "Won polkadot track for making a drag and drop smart contract builder.",
70 | },
71 | {
72 | name: "The Better Hack",
73 | venue: "WeWork Pune",
74 | description:
75 | "Won 2nd prize for making an ai based local seo agent which helps local businesses gain more visibility.",
76 | },
77 | ];
78 |
79 | export const hackathonOrganised = [
80 | {
81 | name: "HackWave 2024",
82 | venue: "CDGI Indore",
83 | description:
84 | "My college's first hackathon. Did everything with a team of 3, from sending proposal to director, all the operations, designing swags, promotion, website ( https://hackwavee.vercel.app/ ) , got over 500 registrations , 40 teams offline in campus for the hackathon.",
85 | },
86 | {
87 | name: "Imagine Hackathon",
88 | venue: "Piwot PanIIT",
89 | description:
90 | "Main organiser of the hackathon with Animesh, handled all the tech stuff onsite and operations in the hackathon. Went to Mumbai while my end sems were going on, did the hackathon, had accident ( fell from train ), came back Indore, gave my end sems, got 7.9 sgpa.",
91 | },
92 | ];
93 |
--------------------------------------------------------------------------------
/components/svgs/mail.tsx:
--------------------------------------------------------------------------------
1 | 'use client';
2 |
3 | import type { Variants } from 'motion/react';
4 | import { motion, useAnimation } from 'motion/react';
5 | import type { HTMLAttributes } from 'react';
6 | import { forwardRef, useCallback, useImperativeHandle, useRef } from 'react';
7 | import { cn } from '@/lib/utils';
8 |
9 | export interface AtSignIconHandle {
10 | startAnimation: () => void;
11 | stopAnimation: () => void;
12 | }
13 |
14 | interface AtSignIconProps extends HTMLAttributes {
15 | size?: number;
16 | }
17 |
18 | const circleVariants: Variants = {
19 | normal: {
20 | opacity: 1,
21 | pathLength: 1,
22 | pathOffset: 0,
23 | transition: {
24 | duration: 0.4,
25 | opacity: { duration: 0.1 },
26 | },
27 | },
28 | animate: {
29 | opacity: [0, 1],
30 | pathLength: [0, 1],
31 | pathOffset: [1, 0],
32 | transition: {
33 | duration: 0.3,
34 | opacity: { duration: 0.1 },
35 | },
36 | },
37 | };
38 |
39 | const pathVariants: Variants = {
40 | normal: {
41 | opacity: 1,
42 | pathLength: 1,
43 | transition: {
44 | delay: 0.3,
45 | duration: 0.3,
46 | opacity: { duration: 0.1, delay: 0.3 },
47 | },
48 | },
49 | animate: {
50 | opacity: [0, 1],
51 | pathLength: [0, 1],
52 | transition: {
53 | delay: 0.3,
54 | duration: 0.3,
55 | opacity: { duration: 0.1, delay: 0.3 },
56 | },
57 | },
58 | };
59 |
60 | const AtSignIcon = forwardRef(
61 | ({ onMouseEnter, onMouseLeave, className, size = 28, ...props }, ref) => {
62 | const controls = useAnimation();
63 | const isControlledRef = useRef(false);
64 |
65 | useImperativeHandle(ref, () => {
66 | isControlledRef.current = true;
67 |
68 | return {
69 | startAnimation: () => controls.start('animate'),
70 | stopAnimation: () => controls.start('normal'),
71 | };
72 | });
73 |
74 | const handleMouseEnter = useCallback(
75 | (e: React.MouseEvent) => {
76 | if (!isControlledRef.current) {
77 | controls.start('animate');
78 | } else {
79 | onMouseEnter?.(e);
80 | }
81 | },
82 | [controls, onMouseEnter]
83 | );
84 |
85 | const handleMouseLeave = useCallback(
86 | (e: React.MouseEvent) => {
87 | if (!isControlledRef.current) {
88 | controls.start('normal');
89 | } else {
90 | onMouseLeave?.(e);
91 | }
92 | },
93 | [controls, onMouseLeave]
94 | );
95 |
96 | return (
97 |
103 |
114 |
121 |
126 |
127 |
128 | );
129 | }
130 | );
131 |
132 | AtSignIcon.displayName = 'AtSignIcon';
133 |
134 | export { AtSignIcon };
135 |
--------------------------------------------------------------------------------
/components/Masonry/Masonry.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import React, { useState, useEffect, useMemo, useRef } from "react";
4 | import { useTransition, a } from "@react-spring/web";
5 |
6 | interface MasonryItem {
7 | id: string | number;
8 | height: number;
9 | image: string;
10 | }
11 |
12 | interface GridItem extends MasonryItem {
13 | x: number;
14 | y: number;
15 | width: number;
16 | height: number;
17 | }
18 |
19 | interface MasonryProps {
20 | data: MasonryItem[];
21 | }
22 |
23 | function Masonry({ data }: MasonryProps) {
24 | const [columns, setColumns] = useState(2);
25 |
26 | useEffect(() => {
27 | const updateColumns = () => {
28 | if (window.matchMedia("(min-width: 1500px)").matches) {
29 | setColumns(5);
30 | } else if (window.matchMedia("(min-width: 1000px)").matches) {
31 | setColumns(4);
32 | } else if (window.matchMedia("(min-width: 600px)").matches) {
33 | setColumns(3);
34 | } else {
35 | setColumns(1); // Mobile devices
36 | }
37 | };
38 |
39 | updateColumns();
40 | window.addEventListener("resize", updateColumns);
41 | return () => window.removeEventListener("resize", updateColumns);
42 | }, []);
43 |
44 | const ref = useRef(null);
45 | const [width, setWidth] = useState(0);
46 |
47 | useEffect(() => {
48 | const handleResize = () => {
49 | if (ref.current) {
50 | setWidth(ref.current.offsetWidth);
51 | }
52 | };
53 |
54 | handleResize();
55 | window.addEventListener("resize", handleResize);
56 | return () => window.removeEventListener("resize", handleResize);
57 | }, []);
58 |
59 | const [heights, gridItems] = useMemo<[number[], GridItem[]]>(() => {
60 | const heights = new Array(columns).fill(0);
61 | const gridItems = data.map((child) => {
62 | const column = heights.indexOf(Math.min(...heights));
63 | const x = (width / columns) * column;
64 | const y = (heights[column] += child.height / 2) - child.height / 2;
65 | return {
66 | ...child,
67 | x,
68 | y,
69 | width: width / columns,
70 | height: child.height / 2,
71 | };
72 | });
73 | return [heights, gridItems];
74 | }, [columns, data, width]);
75 |
76 | const transitions = useTransition<
77 | GridItem,
78 | { x: number; y: number; width: number; height: number; opacity: number }
79 | >(gridItems, {
80 | keys: (item) => item.id,
81 | from: ({ x, y, width, height }) => ({ x, y, width, height, opacity: 0 }),
82 | enter: ({ x, y, width, height }) => ({ x, y, width, height, opacity: 1 }),
83 | update: ({ x, y, width, height }) => ({ x, y, width, height }),
84 | leave: { height: 0, opacity: 0 },
85 | config: { mass: 5, tension: 500, friction: 100 },
86 | trail: 25,
87 | });
88 |
89 | return (
90 |
95 | {transitions((style, item) => (
96 |
101 |
110 |
111 | ))}
112 |
113 | );
114 | }
115 |
116 | export default Masonry;
117 |
--------------------------------------------------------------------------------
/app/project/[name]/page.tsx:
--------------------------------------------------------------------------------
1 | import ImageWithLoader from "@/components/loadingimage";
2 | import BackButton from "@/components/shared/back";
3 | import { Projects } from "@/constants";
4 | import { upperFirst } from "@/utils/uppercase";
5 | import { Metadata } from "next";
6 | import { FiExternalLink, FiGithub } from "react-icons/fi";
7 |
8 | export async function generateStaticParams() {
9 | return Projects.map((p) => ({
10 | name: encodeURIComponent(p.slug),
11 | }));
12 | }
13 | interface Props {
14 | params: {
15 | name: string;
16 | };
17 | }
18 |
19 | export async function generateMetadata({ params }: Props): Promise {
20 | const project = Projects.find(
21 | (p) => encodeURIComponent(p.slug) === encodeURIComponent(params.name)
22 | )!;
23 |
24 | return {
25 | title: project.name,
26 | description: project.description,
27 | openGraph: {
28 | type: "website",
29 | title: project.name,
30 | description: project.description,
31 | images: [
32 | {
33 | url: project.image?.url,
34 | width: project.image?.width,
35 | height: project.image?.height,
36 | alt: project.image?.alt,
37 | },
38 | ],
39 | },
40 | twitter: {
41 | card: "summary_large_image",
42 | site: "@icantcodefyi",
43 | },
44 | };
45 | }
46 |
47 | async function ProjectPage(props: Props) {
48 | const project = Projects.find(
49 | (p) => encodeURIComponent(p.slug) === encodeURIComponent(props.params.name)
50 | )!;
51 | return (
52 |
53 |
54 |
55 |
63 |
64 | {project.image.source ? (
65 |
66 | Source: {project.image.source}
67 |
68 | ) : null}
69 |
70 |
{project.name}
71 |
90 |
91 |
92 |
93 |
{project.description}
94 |
95 | {project.tags?.map((tag) => (
96 |
100 | {upperFirst(tag)}
101 |
102 | ))}
103 |
104 |
105 |
106 | );
107 | }
108 |
109 | export default ProjectPage;
110 |
--------------------------------------------------------------------------------
/components/hackathons.tsx:
--------------------------------------------------------------------------------
1 | import { Projects } from "@/constants";
2 | import Link from "next/link";
3 | import { AiOutlineRight } from "react-icons/ai";
4 | import Masonry from "./Masonry/Masonry";
5 | import { hackathonWins, hackathonOrganised } from "@/constants/hackathons";
6 | const hackathonImages = [
7 | { id: 1, image: "/hackathons/image1.webp", height: 300 },
8 | { id: 2, image: "/hackathons/image2.webp", height: 400 },
9 | { id: 3, image: "/hackathons/image3.webp", height: 300 },
10 | { id: 4, image: "/hackathons/image4.webp", height: 350 },
11 | { id: 5, image: "/hackathons/image5.webp", height: 300 },
12 | { id: 6, image: "/hackathons/image6.webp", height: 350 },
13 | { id: 7, image: "/hackathons/image7.webp", height: 300 },
14 | { id: 8, image: "/hackathons/image8.webp", height: 320 },
15 | { id: 9, image: "/hackathons/image9.webp", height: 300 },
16 | { id: 10, image: "/hackathons/image10.webp", height: 350 },
17 | { id: 11, image: "/hackathons/image11.webp", height: 300 },
18 | { id: 12, image: "/hackathons/image12.webp", height: 400 },
19 | { id: 13, image: "/hackathons/image13.webp", height: 350 },
20 | { id: 14, image: "/hackathons/image14.webp", height: 300 },
21 | { id: 16, image: "/hackathons/image16.webp", height: 350 },
22 | { id: 17, image: "/hackathons/image17.webp", height: 300 },
23 | { id: 18, image: "/hackathons/image18.webp", height: 320 },
24 | { id: 19, image: "/hackathons/image19.webp", height: 300 },
25 | { id: 20, image: "/hackathons/image20.webp", height: 400 },
26 | { id: 21, image: "/hackathons/image21.webp", height: 300 },
27 | ];
28 |
29 | export default function Hackathons() {
30 | return (
31 | <>
32 |
33 |
34 |
35 |
Hackathons Wins
36 | more
37 |
38 |
39 |
40 |
41 | {hackathonWins
42 | .slice(-2)
43 | .reverse()
44 | .map((hack) => (
45 |
46 |
47 |
48 |
49 | {hack.name}
50 |
51 |
52 | {hack.venue}
53 |
54 |
55 |
56 | {hack.description}
57 |
58 |
59 |
60 | ))}
61 |
62 |
63 |
64 |
Hackathons Organised
65 |
66 | {hackathonOrganised
67 | .slice(-2)
68 | .reverse()
69 | .map((hack) => (
70 |
71 |
72 |
73 |
74 | {hack.name}
75 |
76 |
77 | {hack.venue}
78 |
79 |
80 |
81 | {hack.description}
82 |
83 |
84 |
85 | ))}
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 | >
94 | );
95 | }
96 |
--------------------------------------------------------------------------------
/app/hackathons/page.tsx:
--------------------------------------------------------------------------------
1 | import { Projects } from "@/constants";
2 | import Link from "next/link";
3 | import { AiOutlineRight } from "react-icons/ai";
4 | import Masonry from "@/components/Masonry/Masonry";
5 | import { hackathonWins, hackathonOrganised } from "@/constants/hackathons";
6 | import BackButton from "@/components/shared/back";
7 | const hackathonImages = [
8 | { id: 1, image: "/hackathons/image1.webp", height: 300 },
9 | { id: 2, image: "/hackathons/image2.webp", height: 400 },
10 | { id: 3, image: "/hackathons/image3.webp", height: 300 },
11 | { id: 4, image: "/hackathons/image4.webp", height: 350 },
12 | { id: 5, image: "/hackathons/image5.webp", height: 300 },
13 | { id: 6, image: "/hackathons/image6.webp", height: 350 },
14 | { id: 7, image: "/hackathons/image7.webp", height: 300 },
15 | { id: 8, image: "/hackathons/image8.webp", height: 320 },
16 | { id: 9, image: "/hackathons/image9.webp", height: 300 },
17 | { id: 10, image: "/hackathons/image10.webp", height: 350 },
18 | { id: 11, image: "/hackathons/image11.webp", height: 300 },
19 | { id: 12, image: "/hackathons/image12.webp", height: 400 },
20 | { id: 13, image: "/hackathons/image13.webp", height: 350 },
21 | { id: 14, image: "/hackathons/image14.webp", height: 300 },
22 | { id: 16, image: "/hackathons/image16.webp", height: 350 },
23 | { id: 17, image: "/hackathons/image17.webp", height: 300 },
24 | { id: 18, image: "/hackathons/image18.webp", height: 320 },
25 | { id: 19, image: "/hackathons/image19.webp", height: 300 },
26 | { id: 20, image: "/hackathons/image20.webp", height: 400 },
27 | { id: 21, image: "/hackathons/image21.webp", height: 300 },
28 | ];
29 |
30 | export default function Hackathons() {
31 | return (
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
Hackathons Wins
40 |
41 |
42 |
43 | {hackathonWins.reverse().map((hack) => (
44 |
45 |
46 |
47 |
48 | {hack.name}
49 |
50 |
{hack.venue}
51 |
52 |
53 | {hack.description}
54 |
55 |
56 |
57 | ))}
58 |
59 |
60 |
61 |
62 | Hackathons Organised
63 |
64 |
65 | {hackathonOrganised.reverse().map((hack) => (
66 |
67 |
68 |
69 |
70 | {hack.name}
71 |
72 |
{hack.venue}
73 |
74 |
75 | {hack.description}
76 |
77 |
78 |
79 | ))}
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 | );
91 | }
92 |
--------------------------------------------------------------------------------
/components/svgs/github.tsx:
--------------------------------------------------------------------------------
1 | 'use client';
2 |
3 | import type { Variants } from 'motion/react';
4 | import { motion, useAnimation } from 'motion/react';
5 | import type { HTMLAttributes } from 'react';
6 | import { forwardRef, useCallback, useImperativeHandle, useRef } from 'react';
7 | import { cn } from '@/lib/utils';
8 |
9 | export interface GithubIconHandle {
10 | startAnimation: () => void;
11 | stopAnimation: () => void;
12 | }
13 |
14 | interface GithubIconProps extends HTMLAttributes {
15 | size?: number;
16 | }
17 |
18 | const bodyVariants: Variants = {
19 | normal: {
20 | opacity: 1,
21 | pathLength: 1,
22 | scale: 1,
23 | transition: {
24 | duration: 0.3,
25 | },
26 | },
27 | animate: {
28 | opacity: [0, 1],
29 | pathLength: [0, 1],
30 | scale: [0.9, 1],
31 | transition: {
32 | duration: 0.4,
33 | },
34 | },
35 | };
36 |
37 | const tailVariants: Variants = {
38 | normal: {
39 | pathLength: 1,
40 | rotate: 0,
41 | transition: {
42 | duration: 0.3,
43 | },
44 | },
45 | draw: {
46 | pathLength: [0, 1],
47 | rotate: 0,
48 | transition: {
49 | duration: 0.5,
50 | },
51 | },
52 | wag: {
53 | pathLength: 1,
54 | rotate: [0, -15, 15, -10, 10, -5, 5],
55 | transition: {
56 | duration: 2.5,
57 | ease: 'easeInOut',
58 | repeat: Infinity,
59 | },
60 | },
61 | };
62 |
63 | const GithubIcon = forwardRef(
64 | ({ onMouseEnter, onMouseLeave, className, size = 28, ...props }, ref) => {
65 | const bodyControls = useAnimation();
66 | const tailControls = useAnimation();
67 | const isControlledRef = useRef(false);
68 |
69 | useImperativeHandle(ref, () => {
70 | isControlledRef.current = true;
71 |
72 | return {
73 | startAnimation: async () => {
74 | bodyControls.start('animate');
75 | await tailControls.start('draw');
76 | tailControls.start('wag');
77 | },
78 | stopAnimation: () => {
79 | bodyControls.start('normal');
80 | tailControls.start('normal');
81 | },
82 | };
83 | });
84 |
85 | const handleMouseEnter = useCallback(
86 | async (e: React.MouseEvent) => {
87 | if (!isControlledRef.current) {
88 | bodyControls.start('animate');
89 | await tailControls.start('draw');
90 | tailControls.start('wag');
91 | } else {
92 | onMouseEnter?.(e);
93 | }
94 | },
95 | [bodyControls, onMouseEnter, tailControls]
96 | );
97 |
98 | const handleMouseLeave = useCallback(
99 | (e: React.MouseEvent) => {
100 | if (!isControlledRef.current) {
101 | bodyControls.start('normal');
102 | tailControls.start('normal');
103 | } else {
104 | onMouseLeave?.(e);
105 | }
106 | },
107 | [bodyControls, tailControls, onMouseLeave]
108 | );
109 |
110 | return (
111 |
117 |
128 |
134 |
140 |
141 |
142 | );
143 | }
144 | );
145 |
146 | GithubIcon.displayName = 'GithubIcon';
147 |
148 | export { GithubIcon };
149 |
--------------------------------------------------------------------------------
/components/links.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { useRef } from "react";
4 | import {
5 | AtSignIcon,
6 | GithubIcon,
7 | LinkedinIcon,
8 | TwitterIcon,
9 | } from "@/components/svgs";
10 | import type {
11 | AtSignIconHandle,
12 | GithubIconHandle,
13 | LinkedinIconHandle,
14 | TwitterIconHandle,
15 | } from "@/components/svgs";
16 |
17 | export default function Links() {
18 | const twitterIconRef = useRef(null);
19 | const githubIconRef = useRef(null);
20 | const linkedinIconRef = useRef(null);
21 | const atSignIconRef = useRef(null);
22 |
23 | const handleTwitterMouseEnter = () => {
24 | twitterIconRef.current?.startAnimation();
25 | };
26 |
27 | const handleTwitterMouseLeave = () => {
28 | twitterIconRef.current?.stopAnimation();
29 | };
30 |
31 | const handleGithubMouseEnter = () => {
32 | githubIconRef.current?.startAnimation();
33 | };
34 |
35 | const handleGithubMouseLeave = () => {
36 | githubIconRef.current?.stopAnimation();
37 | };
38 |
39 | const handleLinkedinMouseEnter = () => {
40 | linkedinIconRef.current?.startAnimation();
41 | };
42 |
43 | const handleLinkedinMouseLeave = () => {
44 | linkedinIconRef.current?.stopAnimation();
45 | };
46 |
47 | const handleAtSignMouseEnter = () => {
48 | atSignIconRef.current?.startAnimation();
49 | };
50 |
51 | const handleAtSignMouseLeave = () => {
52 | atSignIconRef.current?.stopAnimation();
53 | };
54 |
55 | return (
56 |
129 | );
130 | }
131 |
--------------------------------------------------------------------------------
/components/svgs/linkedin.tsx:
--------------------------------------------------------------------------------
1 | 'use client';
2 |
3 | import { motion, useAnimation } from 'motion/react';
4 | import type { Variants } from 'motion/react';
5 | import type { HTMLAttributes } from 'react';
6 | import { forwardRef, useCallback, useImperativeHandle, useRef } from 'react';
7 | import { cn } from '@/lib/utils';
8 |
9 | export interface LinkedinIconHandle {
10 | startAnimation: () => void;
11 | stopAnimation: () => void;
12 | }
13 |
14 | interface LinkedinIconProps extends HTMLAttributes {
15 | size?: number;
16 | }
17 |
18 | const pathVariants: Variants = {
19 | normal: {
20 | opacity: 1,
21 | pathLength: 1,
22 | pathOffset: 0,
23 | transition: {
24 | duration: 0.4,
25 | opacity: { duration: 0.1 },
26 | },
27 | },
28 | animate: {
29 | opacity: [0, 1],
30 | pathLength: [0, 1],
31 | pathOffset: [1, 0],
32 | transition: {
33 | duration: 0.6,
34 | ease: 'linear',
35 | opacity: { duration: 0.1 },
36 | },
37 | },
38 | };
39 |
40 | const rectVariants: Variants = {
41 | normal: {
42 | opacity: 1,
43 | pathLength: 1,
44 | pathOffset: 0,
45 | transition: {
46 | duration: 0.4,
47 | opacity: { duration: 0.1 },
48 | },
49 | },
50 | animate: {
51 | opacity: [0, 1],
52 | pathLength: [0, 1],
53 | pathOffset: [1, 0],
54 | transition: {
55 | duration: 0.6,
56 | ease: 'linear',
57 | opacity: { duration: 0.1 },
58 | },
59 | },
60 | };
61 |
62 | const circleVariants: Variants = {
63 | normal: {
64 | opacity: 1,
65 | pathLength: 1,
66 | pathOffset: 0,
67 | transition: {
68 | duration: 0.4,
69 | opacity: { duration: 0.1 },
70 | },
71 | },
72 | animate: {
73 | opacity: [0, 1],
74 | pathLength: [0, 1],
75 | pathOffset: [1, 0],
76 | transition: {
77 | duration: 0.6,
78 | ease: 'linear',
79 | opacity: { duration: 0.1 },
80 | },
81 | },
82 | };
83 |
84 | const LinkedinIcon = forwardRef(
85 | ({ onMouseEnter, onMouseLeave, className, size = 28, ...props }, ref) => {
86 | const pathControls = useAnimation();
87 | const rectControls = useAnimation();
88 | const circleControls = useAnimation();
89 |
90 | const isControlledRef = useRef(false);
91 |
92 | useImperativeHandle(ref, () => {
93 | isControlledRef.current = true;
94 |
95 | return {
96 | startAnimation: () => {
97 | pathControls.start('animate');
98 | rectControls.start('animate');
99 | circleControls.start('animate');
100 | },
101 | stopAnimation: () => {
102 | pathControls.start('normal');
103 | rectControls.start('normal');
104 | circleControls.start('normal');
105 | },
106 | };
107 | });
108 |
109 | const handleMouseEnter = useCallback(
110 | (e: React.MouseEvent) => {
111 | if (!isControlledRef.current) {
112 | pathControls.start('animate');
113 | rectControls.start('animate');
114 | circleControls.start('animate');
115 | } else {
116 | onMouseEnter?.(e);
117 | }
118 | },
119 | [circleControls, onMouseEnter, pathControls, rectControls]
120 | );
121 |
122 | const handleMouseLeave = useCallback(
123 | (e: React.MouseEvent) => {
124 | if (!isControlledRef.current) {
125 | pathControls.start('normal');
126 | rectControls.start('normal');
127 | circleControls.start('normal');
128 | } else {
129 | onMouseLeave?.(e);
130 | }
131 | },
132 | [pathControls, rectControls, circleControls, onMouseLeave]
133 | );
134 |
135 | return (
136 |
142 |
153 |
159 |
168 |
176 |
177 |
178 | );
179 | }
180 | );
181 |
182 | LinkedinIcon.displayName = 'LinkedinIcon';
183 |
184 | export { LinkedinIcon };
185 |
--------------------------------------------------------------------------------
/public/oneko.js:
--------------------------------------------------------------------------------
1 | // oneko.js: https://github.com/adryd325/oneko.js
2 |
3 | (function oneko() {
4 | const isReducedMotion =
5 | window.matchMedia(`(prefers-reduced-motion: reduce)`) === true ||
6 | window.matchMedia(`(prefers-reduced-motion: reduce)`).matches === true;
7 |
8 | if (isReducedMotion) return;
9 |
10 | const nekoEl = document.createElement("div");
11 |
12 | let nekoPosX = 32;
13 | let nekoPosY = 32;
14 |
15 | let mousePosX = 0;
16 | let mousePosY = 0;
17 |
18 | let frameCount = 0;
19 | let idleTime = 0;
20 | let idleAnimation = null;
21 | let idleAnimationFrame = 0;
22 |
23 | const nekoSpeed = 10;
24 | const spriteSets = {
25 | idle: [[-3, -3]],
26 | alert: [[-7, -3]],
27 | scratchSelf: [
28 | [-5, 0],
29 | [-6, 0],
30 | [-7, 0],
31 | ],
32 | scratchWallN: [
33 | [0, 0],
34 | [0, -1],
35 | ],
36 | scratchWallS: [
37 | [-7, -1],
38 | [-6, -2],
39 | ],
40 | scratchWallE: [
41 | [-2, -2],
42 | [-2, -3],
43 | ],
44 | scratchWallW: [
45 | [-4, 0],
46 | [-4, -1],
47 | ],
48 | tired: [[-3, -2]],
49 | sleeping: [
50 | [-2, 0],
51 | [-2, -1],
52 | ],
53 | N: [
54 | [-1, -2],
55 | [-1, -3],
56 | ],
57 | NE: [
58 | [0, -2],
59 | [0, -3],
60 | ],
61 | E: [
62 | [-3, 0],
63 | [-3, -1],
64 | ],
65 | SE: [
66 | [-5, -1],
67 | [-5, -2],
68 | ],
69 | S: [
70 | [-6, -3],
71 | [-7, -2],
72 | ],
73 | SW: [
74 | [-5, -3],
75 | [-6, -1],
76 | ],
77 | W: [
78 | [-4, -2],
79 | [-4, -3],
80 | ],
81 | NW: [
82 | [-1, 0],
83 | [-1, -1],
84 | ],
85 | };
86 |
87 | function init() {
88 | nekoEl.id = "oneko";
89 | nekoEl.ariaHidden = true;
90 | nekoEl.style.width = "32px";
91 | nekoEl.style.height = "32px";
92 | nekoEl.style.position = "fixed";
93 | nekoEl.style.pointerEvents = "none";
94 | nekoEl.style.imageRendering = "pixelated";
95 | nekoEl.style.left = `${nekoPosX - 16}px`;
96 | nekoEl.style.top = `${nekoPosY - 16}px`;
97 | nekoEl.style.zIndex = 2147483647;
98 |
99 | let nekoFile = "./oneko.gif"
100 | const curScript = document.currentScript
101 | if (curScript && curScript.dataset.cat) {
102 | nekoFile = curScript.dataset.cat
103 | }
104 | nekoEl.style.backgroundImage = `url(${nekoFile})`;
105 |
106 | document.body.appendChild(nekoEl);
107 |
108 | document.addEventListener("mousemove", function (event) {
109 | mousePosX = event.clientX;
110 | mousePosY = event.clientY;
111 | });
112 |
113 | window.requestAnimationFrame(onAnimationFrame);
114 | }
115 |
116 | let lastFrameTimestamp;
117 |
118 | function onAnimationFrame(timestamp) {
119 | // Stops execution if the neko element is removed from DOM
120 | if (!nekoEl.isConnected) {
121 | return;
122 | }
123 | if (!lastFrameTimestamp) {
124 | lastFrameTimestamp = timestamp;
125 | }
126 | if (timestamp - lastFrameTimestamp > 100) {
127 | lastFrameTimestamp = timestamp
128 | frame()
129 | }
130 | window.requestAnimationFrame(onAnimationFrame);
131 | }
132 |
133 | function setSprite(name, frame) {
134 | const sprite = spriteSets[name][frame % spriteSets[name].length];
135 | nekoEl.style.backgroundPosition = `${sprite[0] * 32}px ${sprite[1] * 32}px`;
136 | }
137 |
138 | function resetIdleAnimation() {
139 | idleAnimation = null;
140 | idleAnimationFrame = 0;
141 | }
142 |
143 | function idle() {
144 | idleTime += 1;
145 |
146 | // every ~ 20 seconds
147 | if (
148 | idleTime > 10 &&
149 | Math.floor(Math.random() * 200) == 0 &&
150 | idleAnimation == null
151 | ) {
152 | let avalibleIdleAnimations = ["sleeping", "scratchSelf"];
153 | if (nekoPosX < 32) {
154 | avalibleIdleAnimations.push("scratchWallW");
155 | }
156 | if (nekoPosY < 32) {
157 | avalibleIdleAnimations.push("scratchWallN");
158 | }
159 | if (nekoPosX > window.innerWidth - 32) {
160 | avalibleIdleAnimations.push("scratchWallE");
161 | }
162 | if (nekoPosY > window.innerHeight - 32) {
163 | avalibleIdleAnimations.push("scratchWallS");
164 | }
165 | idleAnimation =
166 | avalibleIdleAnimations[
167 | Math.floor(Math.random() * avalibleIdleAnimations.length)
168 | ];
169 | }
170 |
171 | switch (idleAnimation) {
172 | case "sleeping":
173 | if (idleAnimationFrame < 8) {
174 | setSprite("tired", 0);
175 | break;
176 | }
177 | setSprite("sleeping", Math.floor(idleAnimationFrame / 4));
178 | if (idleAnimationFrame > 192) {
179 | resetIdleAnimation();
180 | }
181 | break;
182 | case "scratchWallN":
183 | case "scratchWallS":
184 | case "scratchWallE":
185 | case "scratchWallW":
186 | case "scratchSelf":
187 | setSprite(idleAnimation, idleAnimationFrame);
188 | if (idleAnimationFrame > 9) {
189 | resetIdleAnimation();
190 | }
191 | break;
192 | default:
193 | setSprite("idle", 0);
194 | return;
195 | }
196 | idleAnimationFrame += 1;
197 | }
198 |
199 | function frame() {
200 | frameCount += 1;
201 | const diffX = nekoPosX - mousePosX;
202 | const diffY = nekoPosY - mousePosY;
203 | const distance = Math.sqrt(diffX ** 2 + diffY ** 2);
204 |
205 | if (distance < nekoSpeed || distance < 48) {
206 | idle();
207 | return;
208 | }
209 |
210 | idleAnimation = null;
211 | idleAnimationFrame = 0;
212 |
213 | if (idleTime > 1) {
214 | setSprite("alert", 0);
215 | // count down after being alerted before moving
216 | idleTime = Math.min(idleTime, 7);
217 | idleTime -= 1;
218 | return;
219 | }
220 |
221 | let direction;
222 | direction = diffY / distance > 0.5 ? "N" : "";
223 | direction += diffY / distance < -0.5 ? "S" : "";
224 | direction += diffX / distance > 0.5 ? "W" : "";
225 | direction += diffX / distance < -0.5 ? "E" : "";
226 | setSprite(direction, frameCount);
227 |
228 | nekoPosX -= (diffX / distance) * nekoSpeed;
229 | nekoPosY -= (diffY / distance) * nekoSpeed;
230 |
231 | nekoPosX = Math.min(Math.max(16, nekoPosX), window.innerWidth - 16);
232 | nekoPosY = Math.min(Math.max(16, nekoPosY), window.innerHeight - 16);
233 |
234 | nekoEl.style.left = `${nekoPosX - 16}px`;
235 | nekoEl.style.top = `${nekoPosY - 16}px`;
236 | }
237 |
238 | init();
239 | })();
--------------------------------------------------------------------------------
/constants/index.ts:
--------------------------------------------------------------------------------
1 | interface Project {
2 | name: string;
3 | slug: string;
4 | image: {
5 | url: string;
6 | alt: string;
7 | width: number;
8 | height: number;
9 | source?: string;
10 | };
11 | urls: {
12 | githubUrl?: string;
13 | liveUrl?: string;
14 | [key: string]: string | undefined;
15 | };
16 | description: string;
17 | tags: string[];
18 | }
19 |
20 | export const Projects: Project[] = [
21 | {
22 | name: "elafda",
23 | slug: "elafda",
24 | image: {
25 | url: "/elafda.webp",
26 | width: 400,
27 | height: 200,
28 | alt: "E-Lafda Platform - Discussion Platform with Twitter Integration"
29 | },
30 | urls: {
31 | githubUrl: "https://github.com/icantcodefyi/elafda",
32 | liveUrl: "https://elafda.fun/",
33 | },
34 | description: "A comprehensive discussion platform built with Next.js 15, featuring Twitter integration, interactive polls, threaded comments, and modern UI/UX. Includes dynamic e-lafda pages, real-time voting, and rich media support with server components and TypeScript.",
35 | tags: [
36 | "Next.js 15",
37 | "React 19",
38 | "TypeScript",
39 | "Shadcn UI",
40 | "Tailwind CSS"
41 | ]
42 | },
43 | {
44 | name: "doblar",
45 | slug: "doblar",
46 | image: {
47 | url: "/doblar.webp",
48 | width: 400,
49 | height: 200,
50 | alt: "Doblar - Local Image Converter"
51 | },
52 | urls: {
53 | liveUrl: "https://doblar.ani.ink/",
54 | },
55 | description: "Doblar is a fully local image converter. No files are sent anywhere as the conversion is completely local, ensuring privacy and security while converting between different image formats.",
56 | tags: [
57 | "Image Converter",
58 | "Local Processing",
59 | "Privacy",
60 | "Web App",
61 | "TypeScript",
62 | "React"
63 | ]
64 | },
65 | {
66 | name: "annu's poems",
67 | slug: "annu-poems",
68 | image: {
69 | url: "/annu.webp",
70 | width: 400,
71 | height: 200,
72 | alt: "Annu's Poems"
73 | },
74 | urls: {
75 | liveUrl: "https://annnu.art/",
76 | },
77 | description: "A collection of poems written by my precious - Annu.",
78 | tags: [
79 | "Next.js",
80 | "Typescript",
81 | "Next.js",
82 | "Tailwind CSS",
83 | "Shadcn UI",
84 | "React",
85 | "TypeScript",
86 | "Tailwind CSS",
87 | "Shadcn UI",
88 | ]
89 | },
90 | {
91 | name: "invoicely",
92 | slug: "invoicely",
93 | image: {
94 | url: "/invoicely.png",
95 | width: 400,
96 | height: 200,
97 | alt: "Invoicely - Create Beautiful & Professional Invoices"
98 | },
99 | urls: {
100 | liveUrl: "https://invoicely.gg/",
101 | githubUrl: "https://github.com/legions-developer/invoicely"
102 | },
103 | description: "Invoicely is a simple and easy to use invoice generator where you can create beautiful and professional invoices in minutes. ~ Proudly OSS",
104 | tags: [
105 | "Next.js",
106 | "Typescript",
107 | "Pdfjs"
108 | ]
109 | },
110 | {
111 | name: "autodiagram",
112 | slug: "autodiagram",
113 | image: {
114 | url: "/autodiagram.webp",
115 | width: 400,
116 | height: 200,
117 | alt: "AutoDiagram - AI Powered Diagram Generation"
118 | },
119 | urls: {
120 | liveUrl: "https://autodiagram.com/",
121 | githubUrl: "https://github.com/icantcodefyi/diagram"
122 | },
123 | description: "An AI-powered diagram generation tool that transforms text descriptions into professional diagrams. Supports 20+ diagram types including flowcharts, ER diagrams, architecture maps, and sequence diagrams with intelligent suggestions and error prevention.",
124 | tags: [
125 | "AI",
126 | "Diagram Generation",
127 | "Next.js",
128 | "TypeScript",
129 | "SVG",
130 | "SaaS"
131 | ]
132 | },
133 | {
134 | name: "cattype",
135 | slug: "cattype",
136 | image: {
137 | url: "/cattype.webp",
138 | width: 400,
139 | height: 200,
140 | alt: "CatType - A Modern Typing Practice App"
141 | },
142 | urls: {
143 | liveUrl: "https://cattype.live/",
144 | githubUrl: "https://github.com/icantcodefyi/cattype"
145 | },
146 | description: "A modern, customizable typing practice application featuring multiple themes, coding snippets, and real-time statistics. Built with Next.js and TypeScript, it offers various difficulty levels and programming language-specific practice modes.",
147 | tags: [
148 | "Next.js",
149 | "TypeScript",
150 | "Typing Practice",
151 | "Themes",
152 | "Code Snippets",
153 | "Tailwind CSS"
154 | ]
155 | },
156 | {
157 | name: "aurastake",
158 | slug: "aurastake",
159 | image: {
160 | url: "/aurastake.webp",
161 | width: 400,
162 | height: 200,
163 | alt: "Aurastake - Decentralized Productivity Tool"
164 | },
165 | urls: {
166 | liveUrl: "https://aurastake.xyz/",
167 | },
168 | description: "A decentralized productivity tool that uses SOL staking and Aura points to incentivize consistent daily work habits, with public leaderboards and GitHub-style streaks for accountability.",
169 | tags: [
170 | "Productivity",
171 | "Staking",
172 | "Solana",
173 | "Next.js",
174 | "TypeScript",
175 | "Leaderboards"
176 | ]
177 | },
178 | {
179 | name: "getaresume",
180 | slug: "getaresume",
181 | image: {
182 | url: "/getaresume.png",
183 | width: 400,
184 | height: 200,
185 | alt: "GetaResume"
186 | },
187 | urls: {
188 | liveUrl: "https://getaresu.me/"
189 | },
190 | description: "A resume to portfolio website tool that allows you to create a web resume in minutes.",
191 | tags: ["Resume", "PDF", "View", "Portfolio"]
192 | },
193 | {
194 | name: "buildaresume",
195 | slug: "buildaresume",
196 | image: {
197 | url: "/buildaresume.png",
198 | width: 400,
199 | height: 200,
200 | alt: "BuildaResume"
201 | },
202 | urls: {
203 | liveUrl: "https://buildaresume.vercel.app/"
204 | },
205 | description: "A resume builder that allows you to create a resume in minutes based off your linkedin and github profiles.",
206 | tags: ["Resume", "PDF", "View", "Portfolio"]
207 | },
208 | {
209 | name: "school results",
210 | slug: "school-results",
211 | image: {
212 | url: "/lkpsresult.png",
213 | width: 400,
214 | height: 200,
215 | alt: "School Results"
216 | },
217 | description: "An app that generates results for a pre-primary school.",
218 | urls: {
219 | liveUrl: "https://lkps.ani.ink/",
220 | githubUrl: "https://github.com/icantcodefyi/school-results"
221 | },
222 | tags: [
223 | "Education",
224 | "TypeScript",
225 | "Next.Js",
226 | "PDFME",
227 | "Serverless",
228 | "Tailwind Css"
229 | ]
230 | },
231 | {
232 | "name": "linkedin alignment chart",
233 | "slug": "linkedin-chart",
234 | "image": {
235 | "url": "/linkedin.png",
236 | "width": 400,
237 | "height": 200,
238 | "alt": "Linkedin Alignment Chart"
239 | },
240 | "description": "Create D&D-style alignment charts for LinkedIn users. Place users on a Lawful-Chaotic and Good-Evil grid based on AI analysis.",
241 | "urls": {
242 | "liveUrl": "https://linkedin-alignment-chart.vercel.app/",
243 | "githubUrl": "https://github.com/icantcodefyi/linkedin-alignment-chart"
244 | },
245 | tags: ["LinkedIn", "Next.js", "Tailwind CSS", "Vercel AI SDK", "React", "TypeScript"]
246 | },
247 | {
248 | name: "glanza labs",
249 | slug: "glanza-labs",
250 | image: {
251 | url: "/glanza.png",
252 | width: 400,
253 | height: 200,
254 | alt: "Glanza Labs"
255 | },
256 | description: "Beautifully designed and developed landing page including app for Glanza Labs.",
257 | urls: {
258 | liveUrl: "https://www.glanza.org/"
259 | },
260 | tags: [
261 | "Landing Page",
262 | "NextAuth",
263 | "Next.Js",
264 | "Tailwind Css",
265 | "PostgreSQL"
266 | ]
267 | },
268 | {
269 | name: "nexus visualize",
270 | slug: "nexus-visualize",
271 | image: {
272 | url: "/nexusdashboard.png",
273 | width: 400,
274 | height: 200,
275 | alt: "Nexus Visualize"
276 | },
277 | description: "AI Dashboard Generator is an open-source dashboard generator based on Next.js and OpenAI's GPT-3 technology. The tool allows users to feed existing tables or datasets, and from there, it understands the intent of the table and generates a visual representation of the data.",
278 | urls: {
279 | liveUrl: "https://dashboard.aniruddh.tech",
280 | githubUrl: "https://github.com/AnuPlayz/nexus-dashboard"
281 | },
282 | tags: [
283 | "AI",
284 | "Next.js",
285 | "OpenAI",
286 | "GPT-3.5",
287 | "Data Visualization",
288 | "CSV"
289 | ]
290 | },
291 | {
292 | name: "nexus events",
293 | slug: "nexus-events",
294 | image: {
295 | url: "/nexusevents.png",
296 | width: 400,
297 | height: 200,
298 | alt: "Nexus Events",
299 | },
300 | description:
301 | "A event management nft ticketing dapp made using PolygonId, The Graph and Scroll",
302 | urls: {
303 | liveUrl: "https://nexus-events.vercel.app/",
304 | githubUrl: "https://github.com/orgs/NexusTech-India/repositories"
305 | },
306 | tags: [
307 | "Solidity",
308 | "Hardhat",
309 | "Typescript",
310 | "Next.js",
311 | "PolygonId",
312 | "TheGraph",
313 | "Scroll.io",
314 | ],
315 | },
316 | {
317 | name: "nexus read",
318 | slug: "nexus-read",
319 | image: {
320 | url: "/nexusread.png",
321 | alt: "NexusRead",
322 | width: 400,
323 | height: 200,
324 | },
325 | urls: {
326 | githubUrl: "https://github.com/AnuPlayz/nexus-read",
327 | liveUrl: "https://www.youtube.com/watch?v=5FMkSz6AF-0"
328 | },
329 | description:"A manga hosting web application for hosting your own manga and showcase that to the world!",
330 | tags: [
331 | "Next.js",
332 | "TypeScript",
333 | "Tailwind CSS",
334 | "NextAuth",
335 | "Prisma",
336 | "MongoDB",
337 | "Imagga"
338 | ],
339 | },
340 | {
341 | name: "citronics",
342 | description:
343 | "An official event website for college fest made with html css and vanilla js",
344 | slug: "citronics",
345 | urls: {
346 | liveUrl: "https://citronics.netlify.app/",
347 | },
348 | image: {
349 | url: "/citronics.png",
350 | width: 400,
351 | height: 200,
352 | alt: "Citronics",
353 | },
354 | tags: [
355 | "HTML5",
356 | "CSS3",
357 | "Javascript",
358 | "Nodejs",
359 | "ParticleJs",
360 | "Nodejs",
361 | ],
362 | },
363 | {
364 | name: "investment bot",
365 | slug: "investment-bot",
366 | urls: {
367 | githubUrl: "https://github.com/AnuPlayz/investment-bot",
368 | },
369 | image: {
370 | url: "/investment.png",
371 | alt: "Investment app",
372 | width: 400,
373 | height: 200,
374 | },
375 | description: "A discord bot for discord.gg/paisa ( investment server ) built using discord.js and TypeScript.",
376 | tags: ["TypeScript", "Discord.js", "Node.js"],
377 | },
378 | {
379 | name: "animax",
380 | slug: "animax",
381 | urls: {
382 | githubUrl: "https://github.com/AnuPlayz/animax",
383 | },
384 | tags: ["Express", "MongoDB", "Typescript", "Javascript", "Node.js"],
385 | description: "Backend for an anime website + a scraper which gets all data of anime from websites like aniwatch.to .",
386 | image: {
387 | url: "/animax.png",
388 | width: 400,
389 | height: 200,
390 | alt: "React Cli",
391 | },
392 | },
393 | ];
394 |
--------------------------------------------------------------------------------
/public/leapflow.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/app/page.tsx:
--------------------------------------------------------------------------------
1 | import Transition from "@/components/transition";
2 | import { AiOutlineRight } from "react-icons/ai";
3 | import { AtSignIcon, GithubIcon, LinkedinIcon } from "@/components/svgs";
4 | import { Projects } from "@/constants";
5 | import Badge from "@/components/badge";
6 | import Image from "next/image";
7 | import Link from "next/link";
8 | import Hackathons from "@/components/hackathons";
9 | import WorkExperience from "@/components/workexp";
10 | import Links from "@/components/links";
11 |
12 | export default function Home() {
13 | const birthDate = new Date("2005-03-29");
14 | const today = new Date();
15 | const age =
16 | today.getFullYear() -
17 | birthDate.getFullYear() -
18 | (today.getMonth() < birthDate.getMonth() ||
19 | (today.getMonth() === birthDate.getMonth() &&
20 | today.getDate() < birthDate.getDate())
21 | ? 1
22 | : 0);
23 |
24 | return (
25 |
26 |
27 |
28 |
35 |
36 |
Aniruddh
37 | @icantcodefyi
38 |
39 |
40 |
41 |
42 |
43 |
44 | I make
45 |
46 |
47 |
48 | I'm Aniruddh, a {age} year old developer living in India. I am
49 | a self-taught developer who loves to code and make things.
50 |
51 |
52 | messing with{" "}
53 |
54 |
63 |
74 |
75 |
76 |
77 |
85 |
89 |
93 |
94 |
95 |
103 |
104 |
105 |
106 |
114 |
115 |
116 |
117 |
118 |
119 | Next.js
120 |
121 | ,{" "}
122 |
123 |
132 |
133 |
134 |
145 |
146 |
147 | Nest.js
148 |
149 | ,{" "}
150 |
151 |
159 |
165 |
166 | Prisma
167 |
168 | ,{" "}
169 |
170 |
178 |
182 |
183 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 | Solana
201 | {" "}
202 | and some other tooling.
203 |
204 |
205 |
206 |
211 |
212 |
213 |
214 |
Available for new opportunities
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
Projects
226 |
227 | more
228 |
229 |
230 |
231 | {Projects.slice(0, 5).map((project) => (
232 |
237 |
238 |
239 |
240 | {project.name}
241 |
242 |
243 |
244 |
248 |
249 |
250 | ))}
251 |
252 |
253 |
254 |
255 |
259 | Resume
260 |
261 |
262 |
263 |
264 | );
265 | }
266 |
--------------------------------------------------------------------------------