-
- I am a based in and currently a pursuing a
- major at the .
- I am passionate about tech and innovation, always exploring the
- intersection of software
- development and AI.
+
+
+ About
+
+
+
+
+
+ I am a based in{' '}
+ and currently a {' '}
+ pursuing a
+ major at the{' '}
+ . I am passionate about tech and innovation,
+ always exploring the{' '}
+
+ intersection of software development and AI.
-
- {/*
+
+ {/*
Outside of academics and professional career, I enjoy contributing to open-source projects, working on side projects, participating in hackathons and playing
table tennis, football, cricket and badminton. I also like to travel & explore new places, cuisines & restaurants.
*/}
- {/*
+ {/*
You can find me any one of these TV series:
,
,
@@ -37,36 +44,49 @@ const About = () => {
,
.
*/}
-
- You can find me any one of these series:
- ,
- ,
- and .
-
-
- For those of you curious, the favicon is a made via Python, using NetworkX and Matplotlib.
- Check the
- image
-
- out, learn
- more
- about hypercube graphs & here is the
- code
- to generate it yourself.
-
-
-
+
+ You can find me any one of these
+ series:
+
+ ,
+
+ ,
+ and{' '}
+ .
+
+
+ For those of you curious, the favicon is a{' '}
+ made via Python, using NetworkX and
+ Matplotlib. Check the{' '}
+
+ image
+
+ out, learn{' '}
+
+ more
+ {' '}
+ about hypercube graphs & here is the{' '}
+
+ code
+ {' '}
+ to generate it yourself.
+
+
+
- )
-}
+ );
+};
-export default About
+export default About;
diff --git a/src/components/Education.tsx b/src/components/Education.tsx
index 72fde60..0965c58 100644
--- a/src/components/Education.tsx
+++ b/src/components/Education.tsx
@@ -1,60 +1,80 @@
-import { useMediaQuery } from '@/hooks/useMediaQuery'
-import LinkWithArrow from "@/components/ui/LinkWithArrow";
-import BlurFade, { BLUR_FADE_DELAY } from '@/components/ui/BlurFade'
-import { cn } from '@/lib/utils'
+import BlurFade, { BLUR_FADE_DELAY } from '@/components/ui/BlurFade';
+import LinkWithArrow from '@/components/ui/LinkWithArrow';
+import { useMediaQuery } from '@/hooks/useMediaQuery';
+import { cn } from '@/lib/utils';
export function Education() {
- const isDesktop = useMediaQuery('(min-width: 768px)')
- return (
-
-
- Education
-
-
-
-
-
- {/* // center the bullet : `top-1/2 transform -translate-y-1/2` */}
-
-
-
-
-
-
-
- University of Waterloo
-
-
-
Bachelor of Mathematics, Computational Mathematics Major
- {(!isDesktop) && Sept 2022 - Present }
-
- {isDesktop && (
Sept 2022 - Present )}
-
-
-
+ const isDesktop = useMediaQuery('(min-width: 768px)');
+ return (
+
+
+
+ Education
+
+
+
+
+
+
+ {/* // center the bullet : `top-1/2 transform -translate-y-1/2` */}
+
+
+
+
+
+
+
+ University of Waterloo
+
+
+
+ Bachelor of Mathematics, Computational Mathematics Major
+
+ {!isDesktop && (
+
+ Sept 2022 - Present
+
+ )}
-
-
- Computational Mathematics
- is a interdisplinary major that combines Mathematics, Statistics, Optimization and Computer Science, offered by the Faculty of Mathematics.
- {/* TODO: Insert all courses using a recursive React component */}
-
-
-
- )
+ {isDesktop && (
+
+ Sept 2022 - Present
+
+ )}
+
+
+
+
+
+
+ Computational Mathematics
+ {' '}
+ is a interdisplinary major that combines Mathematics, Statistics, Optimization and
+ Computer Science, offered by the Faculty of Mathematics.
+ {/* TODO: Insert all courses using a recursive React component */}
+
+
+
+ );
}
diff --git a/src/components/Experiences.tsx b/src/components/Experiences.tsx
index 4461091..f199ece 100644
--- a/src/components/Experiences.tsx
+++ b/src/components/Experiences.tsx
@@ -1,39 +1,42 @@
-'use client'
+'use client';
+
+import { useState } from 'react';
-import { useState } from 'react'
-import { ExperienceTileProps, ExperienceTile } from './Tiles/ExperienceTile'
import BlurFade, { BLUR_FADE_DELAY } from '@/components/ui/BlurFade';
import { cn } from '@/lib/utils';
+import { ExperienceTileProps, ExperienceTile } from './Tiles/ExperienceTile';
+
interface ExperiencesProps {
- arr: ExperienceTileProps[];
+ arr: ExperienceTileProps[];
}
export function Experiences({ arr }: ExperiencesProps) {
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- const [experiences, setExperiences] = useState(arr)
- return (
-
-
- Experience
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ const [experiences, setExperiences] = useState(arr);
+ return (
+
+
+
+ Experience
+
+
+ {experiences && experiences.length > 0 ? (
+
+ {experiences.map((experience, index) => (
+
+
- {experiences && experiences.length > 0 ? (
-
- {experiences.map((experience, index) => (
-
-
-
- ))}
-
- ) : (
-
No experiences to display.
- )}
-
- )
-}
\ No newline at end of file
+ ))}
+
+ ) : (
+ No experiences to display.
+ )}
+
+ );
+}
diff --git a/src/components/Footer.tsx b/src/components/Footer.tsx
index ecbc325..1b229b5 100644
--- a/src/components/Footer.tsx
+++ b/src/components/Footer.tsx
@@ -1,42 +1,49 @@
-import LinkWithArrow from "@/components/ui/LinkWithArrow";
-import { detailsForMetadata, personalDetails } from "@/data/resume"
-import { cn } from "@/lib/utils"
+import LinkWithArrow from '@/components/ui/LinkWithArrow';
+import { detailsForMetadata, personalDetails } from '@/data/resume';
+import { cn } from '@/lib/utils';
const Footer = () => {
return (
-
-
-
-
- github
-
-
- linkedin
-
-
- email
-
+
- )
-}
+ );
+};
-export default Footer
+export default Footer;
diff --git a/src/components/Header.tsx b/src/components/Header.tsx
index 6f1de8a..d8e674a 100644
--- a/src/components/Header.tsx
+++ b/src/components/Header.tsx
@@ -1,137 +1,164 @@
-import { Copy, CopyCheck } from 'lucide-react'
-import { useMediaQuery } from '@/hooks/useMediaQuery'
-import LinkWithArrow from "@/components/ui/LinkWithArrow";
-import BlurFade, { BLUR_FADE_DELAY } from '@/components/ui/BlurFade'
-// import Image from 'next/image'
-
-import { useEffect, useState } from 'react';
import copy from 'copy-to-clipboard';
+import { Copy, CopyCheck } from 'lucide-react';
+import { useEffect, useState } from 'react';
+
+import BlurFade, { BLUR_FADE_DELAY } from '@/components/ui/BlurFade';
+import LinkWithArrow from '@/components/ui/LinkWithArrow';
+import { useMediaQuery } from '@/hooks/useMediaQuery';
+// import Image from 'next/image'
export type HeaderProps = {
- name: string,
- pronouns: string,
- currentEducation: string,
- currentJob?: string[],
- basedFrom?: string,
- githubLink: string,
- linkedinLink: string,
- email: string,
- resumeFile: string,
-}
+ name: string;
+ pronouns: string;
+ currentEducation: string;
+ currentJob?: string[];
+ basedFrom?: string;
+ githubLink: string;
+ linkedinLink: string;
+ email: string;
+ resumeFile: string;
+};
-const Header = ({name, pronouns, currentEducation, currentJob, basedFrom, githubLink, linkedinLink, email, resumeFile}: HeaderProps) => {
- const isDesktop = useMediaQuery('(min-width: 768px)')
- const [copied, setCopiedId] = useState
();
- useEffect(() => { setTimeout(() => { setCopiedId(undefined); }, 3000) }, [copied]);
+const Header = ({
+ name,
+ pronouns,
+ currentEducation,
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ currentJob,
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ basedFrom,
+ githubLink,
+ linkedinLink,
+ email,
+ resumeFile,
+}: HeaderProps) => {
+ const isDesktop = useMediaQuery('(min-width: 768px)');
+ const [copied, setCopiedId] = useState();
+ useEffect(() => {
+ setTimeout(() => {
+ setCopiedId(undefined);
+ }, 3000);
+ }, [copied]);
- return (
-
-
-
- {(!isDesktop) && (
-
-
-
- )}
-
-
-
-
-
{name}
-
-
-
-
-
- github
-
-
-
-
- linkedin
-
-
-
-
- email
-
-
-
-
- resume
-
-
-
-
- {pronouns && (
- {pronouns}
- )}
- {currentEducation && (
- {currentEducation}
- )}
-
-
-
- {email.split('').map(
- (char) => {
- return (char === '@') ? "[at]" : char;
- }
- )}
-
-
{
- if ('clipboard' in navigator) {
- await navigator.clipboard.writeText(email);
- } else {
- copy(email);
- }
- setCopiedId('copied-email');
- }}
- >
- {
- copied === 'copied-email'
- ?
- :
- }
-
-
-
-
-
-
- {(isDesktop) && (
-
-
-
- )}
-
+ return (
+
+
+
+ {!isDesktop && (
+
+
+
+ )}
+
+
+
+
+
{name}
- {/*
+
+
+
+
+ github
+
+
+
+
+ linkedin
+
+
+
+
+ email
+
+
+
+
+ resume
+
+
+
+
+ {pronouns && (
+
+ {pronouns}
+
+ )}
+ {currentEducation && (
+
+ {currentEducation}
+
+ )}
+
+
+
+ {email.split('').map((char) => {
+ return char === '@' ? '[at]' : char;
+ })}
+
+
{
+ if ('clipboard' in navigator) {
+ await navigator.clipboard.writeText(email);
+ } else {
+ copy(email);
+ }
+ setCopiedId('copied-email');
+ }}
+ >
+ {copied === 'copied-email' ? (
+
+ ) : (
+
+ )}
+
+
+
+
+
+
+ {isDesktop && (
+
+
+
+ )}
+
+
+ {/*
About
@@ -142,8 +169,8 @@ const Header = ({name, pronouns, currentEducation, currentJob, basedFrom, github
*/}
-
- )
-}
+
+ );
+};
-export default Header
+export default Header;
diff --git a/src/components/Miscellaneous.tsx b/src/components/Miscellaneous.tsx
index 2e48a9e..6485ba5 100644
--- a/src/components/Miscellaneous.tsx
+++ b/src/components/Miscellaneous.tsx
@@ -1,14 +1,14 @@
const misc = () => {
- return (
-
- Misc.
-
- Winner of MSU Hackathon 2023
- Volunteer coding instructor for local high school students
- Avid rock climber and nature photographer
-
-
- )
-}
+ return (
+
+ Misc.
+
+ Winner of MSU Hackathon 2023
+ Volunteer coding instructor for local high school students
+ Avid rock climber and nature photographer
+
+
+ );
+};
-export default misc
+export default misc;
diff --git a/src/components/Navbar.tsx b/src/components/Navbar.tsx
index b8a56bc..8931b97 100644
--- a/src/components/Navbar.tsx
+++ b/src/components/Navbar.tsx
@@ -1,31 +1,51 @@
-'use client'
+'use client';
+import Link from 'next/link';
+
+import HoverHighlight from '@/components/ui/HoverHighlighter';
import { cn } from '@/lib/utils';
+
import ThemeSwitch from './ThemeSwitch';
-import HoverHighlight from '@/components/ui/HoverHighlighter';
-import Link from 'next/link';
const Navbar = () => {
- // const completion = useReadingProgress();
+ // const completion = useReadingProgress();
- return (
-
- )
-}
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
-export default Navbar
\ No newline at end of file
+export default Navbar;
diff --git a/src/components/Projects.tsx b/src/components/Projects.tsx
index 84f211b..7f6c462 100644
--- a/src/components/Projects.tsx
+++ b/src/components/Projects.tsx
@@ -1,42 +1,43 @@
-import { useState } from 'react'
-import { ProjectTile, ProjectTileProps } from './Tiles/ProjectTile'
+import { useState } from 'react';
+
import BlurFade, { BLUR_FADE_DELAY } from '@/components/ui/BlurFade';
import { cn } from '@/lib/utils';
+import { ProjectTile, ProjectTileProps } from './Tiles/ProjectTile';
+
interface ProjectProps {
- arr: ProjectTileProps[];
+ arr: ProjectTileProps[];
}
export function Projects({ arr }: ProjectProps) {
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- const [projects, setProjects] = useState(arr)
- return (
-
-
- Projects
-
- {projects && projects.length > 0 ? (
-
- {
- projects.map((project, index) => {
- project.delayTime = BLUR_FADE_DELAY * 12 + index * 0.2;
- return (
-
-
-
- )
- })
- }
-
- ) : (
- No projects to display.
- )}
-
- )
-}
\ No newline at end of file
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ const [projects, setProjects] = useState(arr);
+ return (
+
+
+
+ Projects
+
+
+ {projects && projects.length > 0 ? (
+
+ {projects.map((project, index) => {
+ project.delayTime = BLUR_FADE_DELAY * 12 + index * 0.2;
+ return (
+
+
+
+ );
+ })}
+
+ ) : (
+ No projects to display.
+ )}
+
+ );
+}
diff --git a/src/components/Skills.tsx b/src/components/Skills.tsx
index 8632d1b..fc81c0e 100644
--- a/src/components/Skills.tsx
+++ b/src/components/Skills.tsx
@@ -1,75 +1,68 @@
-import { Badge } from '@/components/ui/Badge';
+import { Icon } from '@iconify/react';
import { useState } from 'react';
+
+import { Badge } from '@/components/ui/Badge';
import BlurFade, { BLUR_FADE_DELAY } from '@/components/ui/BlurFade';
-import { cn } from '@/lib/utils';
-import { Icon } from '@iconify/react';
import { techIcons } from '@/lib/techIcons';
+import { cn } from '@/lib/utils';
interface SkillsProps {
- arr: {
- languages: string[];
- frameworksAndLibraries: string[];
- databases?: string[];
- tools: string[];
- };
+ arr: {
+ languages: string[];
+ frameworksAndLibraries: string[];
+ databases?: string[];
+ tools: string[];
+ };
}
export function Skills({ arr }: SkillsProps) {
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- const [skills, setSkills] = useState(arr)
- const result = Object.entries(skills).map(([key, value], idx) => {
- return (
-
-
- {
-
- {(key === "frameworksAndLibraries")
- ? (
- ('databases' in skills)
- ? `Frameworks & Libraries`
- : `Frameworks, Libraries & Databases`
- )
- : key.charAt(0).toUpperCase() + key.slice(1)}:
-
- }
-
-
- {
- value.map((skillText, index) => (
-
-
- {
- techIcons?.[skillText] &&
- {
-
- }
- }
- {skillText}
-
-
- ))
- }
-
-
- );
- });
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ const [skills, setSkills] = useState(arr);
+ const result = Object.entries(skills).map(([key, value], idx) => {
return (
-
-
- Skills
+
+
+ {
+
+ {key === 'frameworksAndLibraries'
+ ? 'databases' in skills
+ ? 'Frameworks & Libraries'
+ : 'Frameworks, Libraries & Databases'
+ : key.charAt(0).toUpperCase() + key.slice(1)}
+ :
+
+ }
+
+
+ {value.map((skillText, index) => (
+
+
+ {techIcons?.[skillText] && (
+
+ { }
+
+ )}
+ {skillText}
+
- {skills ? result : (
-
No skills to display.
- )}
-
- )
+ ))}
+
+
+ );
+ });
+ return (
+
+
+
+ Skills
+
+
+ {skills ? result : No skills to display.
}
+
+ );
}
diff --git a/src/components/SmoothScrollProvider.tsx b/src/components/SmoothScrollProvider.tsx
index dab74ed..cdc85e7 100644
--- a/src/components/SmoothScrollProvider.tsx
+++ b/src/components/SmoothScrollProvider.tsx
@@ -1,8 +1,14 @@
-'use client'
+'use client';
-import { useSmoothScroll } from '@/hooks/useSmoothScroll'
+import { useSmoothScroll } from '@/hooks/useSmoothScroll';
-export function SmoothScrollProvider({ children, offset = 0 }: { children: React.ReactNode, offset?: number }) {
- useSmoothScroll(offset)
- return <>{children}>
+export function SmoothScrollProvider({
+ children,
+ offset = 0,
+}: {
+ children: React.ReactNode;
+ offset?: number;
+}) {
+ useSmoothScroll(offset);
+ return <>{children}>;
}
diff --git a/src/components/ThemeSwitch.tsx b/src/components/ThemeSwitch.tsx
index ddb4557..c15c5bf 100644
--- a/src/components/ThemeSwitch.tsx
+++ b/src/components/ThemeSwitch.tsx
@@ -1,29 +1,30 @@
-"use client";
+'use client';
-import { Moon, Sun } from 'lucide-react'
-import { useState, useEffect } from 'react'
-import { useTheme } from 'next-themes'
+import { Moon, Sun } from 'lucide-react';
+import { useTheme } from 'next-themes';
+import { useState, useEffect } from 'react';
// import Image from "next/image";
export default function ThemeSwitch() {
- const [mounted, setMounted] = useState(false)
- const { setTheme, resolvedTheme } = useTheme()
+ const [mounted, setMounted] = useState(false);
+ const { setTheme, resolvedTheme } = useTheme();
- useEffect(() => setMounted(true), [])
+ useEffect(() => setMounted(true), []);
if (!mounted) return null;
return (
{
- setTheme(resolvedTheme === "dark" ? "light" : "dark");
- }}>
- {
- resolvedTheme === 'dark'
- ?
- :
- }
+ className="cursor-pointer"
+ onClick={() => {
+ setTheme(resolvedTheme === 'dark' ? 'light' : 'dark');
+ }}
+ >
+ {resolvedTheme === 'dark' ? (
+
+ ) : (
+
+ )}
- )
+ );
}
diff --git a/src/components/Tiles/ExperienceTile.tsx b/src/components/Tiles/ExperienceTile.tsx
index 3545c80..e5314c0 100644
--- a/src/components/Tiles/ExperienceTile.tsx
+++ b/src/components/Tiles/ExperienceTile.tsx
@@ -1,64 +1,75 @@
-import LinkWithArrow from "@/components/ui/LinkWithArrow";
-import { useMediaQuery } from '@/hooks/useMediaQuery'
+import LinkWithArrow from '@/components/ui/LinkWithArrow';
+import { useMediaQuery } from '@/hooks/useMediaQuery';
// import Image from 'next/image';
export type ExperienceTileProps = {
- companyLogo: string
- companyName: string
- companyLink: string
- position: string
- period: string
- responsibilities?: string[]
-}
-
-
+ companyLogo: string;
+ companyName: string;
+ companyLink: string;
+ position: string;
+ period: string;
+ responsibilities?: string[];
+};
-export function ExperienceTile({ companyLogo, companyName, companyLink, position, period, responsibilities }: ExperienceTileProps) {
- const isDesktop = useMediaQuery('(min-width: 768px)')
+export function ExperienceTile({
+ companyLogo,
+ companyName,
+ companyLink,
+ position,
+ period,
+ responsibilities,
+}: ExperienceTileProps) {
+ const isDesktop = useMediaQuery('(min-width: 768px)');
return (
- 0) && !isDesktop ? "-mb-6" : "mb-6"
- ) + " " + "flex items-center"
- }>
-
-
- {/* // center the bullet : `top-1/2 transform -translate-y-1/2` */}
-
-
-
-
-
-
{position}
-
-
- {companyName}
-
-
- {(!isDesktop) && {period} }
-
- {isDesktop && (
{period} )}
-
- {responsibilities && responsibilities.length > 0 &&
-
-
- {responsibilities.map((responsibility, index) => (
- - {responsibility}
- ))}
-
-
- }
+
0) && !isDesktop ? '-mb-6' : 'mb-6') +
+ ' ' +
+ 'flex items-center'
+ }
+ >
+
+
+ {/* // center the bullet : `top-1/2 transform -translate-y-1/2` */}
+
+
+
+
+
+
{position}
+
+ {companyName}
+
+ {!isDesktop && (
+ {period}
+ )}
+
+ {isDesktop && (
+
{period}
+ )}
+
+ {responsibilities && responsibilities.length > 0 && (
+
+
+ {responsibilities.map((responsibility, index) => (
+ - {responsibility}
+ ))}
+
+ )}
+
- )
-}
\ No newline at end of file
+ );
+}
diff --git a/src/components/Tiles/ProjectTile.tsx b/src/components/Tiles/ProjectTile.tsx
index 093e009..d3e85dc 100644
--- a/src/components/Tiles/ProjectTile.tsx
+++ b/src/components/Tiles/ProjectTile.tsx
@@ -1,44 +1,58 @@
import { Icon } from '@iconify/react';
-import { techIcons } from '@/lib/techIcons';
+
import { Badge } from '@/components/ui/Badge';
import BlurFade from '@/components/ui/BlurFade';
-import LinkWithArrow from "@/components/ui/LinkWithArrow";
+import LinkWithArrow from '@/components/ui/LinkWithArrow';
+import { techIcons } from '@/lib/techIcons';
type ProjectTileDetailProps = {
- title: string
- description: string
- repo: boolean,
- repoUrl?: string,
- live: boolean,
- liveUrl?: string,
- techStack: string[]
-}
+ title: string;
+ description: string;
+ repo: boolean;
+ repoUrl?: string;
+ live: boolean;
+ liveUrl?: string;
+ techStack: string[];
+};
export type ProjectTileProps = ProjectTileDetailProps & {
delayTime?: number;
};
-export function ProjectTile({ delayTime, title, description, repo, repoUrl, live, liveUrl, techStack }: ProjectTileProps) {
+export function ProjectTile({
+ delayTime,
+ title,
+ description,
+ repo,
+ repoUrl,
+ live,
+ liveUrl,
+ techStack,
+}: ProjectTileProps) {
// const isDesktop = useMediaQuery('(min-width: 768px)')
return (
-
+
{title}
-
+
{repo && (
repo
)}
{live && (
link
@@ -47,37 +61,32 @@ export function ProjectTile({ delayTime, title, description, repo, repoUrl, live
{description}
{techStack && techStack.length > 0 && (
-
- {techStack.map((skillText, index) => (
- delayTime ? (
-
-
- {
- techIcons?.[skillText] &&
- {
-
- }
- }
- {skillText}
-
-
- ) : (
-
- {
- techIcons?.[skillText] &&
- {
-
- }
- }
- {skillText}
-
- )
- ))}
-
+
+ {techStack.map((skillText, index) =>
+ delayTime ? (
+
+
+ {techIcons?.[skillText] && (
+
+ { }
+
+ )}
+ {skillText}
+
+
+ ) : (
+
+ {techIcons?.[skillText] && (
+
+ { }
+
+ )}
+ {skillText}
+
+ ),
+ )}
+
)}
- )
-}
\ No newline at end of file
+ );
+}
diff --git a/src/components/ui/BackButton.tsx b/src/components/ui/BackButton.tsx
index 61d6313..0d2aa4f 100644
--- a/src/components/ui/BackButton.tsx
+++ b/src/components/ui/BackButton.tsx
@@ -1,17 +1,18 @@
-"use client";
+'use client';
-import { cn } from "@/lib/utils";
-import { useRouter } from "next/navigation";
+import { useRouter } from 'next/navigation';
+import { cn } from '@/lib/utils';
-export default function BackButton({ className, ...props }: React.ButtonHTMLAttributes
) {
+export default function BackButton({
+ className,
+ ...props
+}: React.ButtonHTMLAttributes) {
const router = useRouter();
const hasHistory =
- (typeof window !== "undefined" &&
- window.history?.length &&
- window.history.length > 1) ||
- typeof window === "undefined";
+ (typeof window !== 'undefined' && window.history?.length && window.history.length > 1) ||
+ typeof window === 'undefined';
if (!hasHistory) {
return null;
@@ -19,15 +20,13 @@ export default function BackButton({ className, ...props }: React.ButtonHTMLAttr
return (
);
-}
\ No newline at end of file
+}
diff --git a/src/components/ui/Badge.tsx b/src/components/ui/Badge.tsx
index ebb79c2..9f51d34 100644
--- a/src/components/ui/Badge.tsx
+++ b/src/components/ui/Badge.tsx
@@ -1,34 +1,32 @@
-import * as React from "react"
-import { cva, type VariantProps } from "class-variance-authority"
+import { cva, type VariantProps } from 'class-variance-authority';
+import * as React from 'react';
-import { cn } from "@/lib/utils"
+import { cn } from '@/lib/utils';
const badgeVariants = cva(
- "flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
+ 'flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2',
{
variants: {
variant: {
default:
- "border-transparent shadow bg-neutral-950 text-gray-200 hover:bg-neutral-950/80 dark:bg-neutral-100 dark:text-gray-800 dark:hover:bg-neutral-100/80 focus:bg-neutral-900 dark:focus:bg-neutral-200",
+ 'border-transparent shadow bg-neutral-950 text-gray-200 hover:bg-neutral-950/80 dark:bg-neutral-100 dark:text-gray-800 dark:hover:bg-neutral-100/80 focus:bg-neutral-900 dark:focus:bg-neutral-200',
secondary:
- "border-transparent shadow bg-neutral-200 text-gray-800 hover:bg-neutral-200/80 dark:bg-neutral-700 dark:text-gray-200 dark:hover:bg-neutral-700/80",
- outline: "text-foreground focus:bg-accent focus:text-accent-foreground",
+ 'border-transparent shadow bg-neutral-200 text-gray-800 hover:bg-neutral-200/80 dark:bg-neutral-700 dark:text-gray-200 dark:hover:bg-neutral-700/80',
+ outline: 'text-foreground focus:bg-accent focus:text-accent-foreground',
},
},
defaultVariants: {
- variant: "default",
+ variant: 'default',
},
- }
-)
+ },
+);
export interface BadgeProps
extends React.HTMLAttributes,
VariantProps {}
function Badge({ className, variant, ...props }: BadgeProps) {
- return (
-
- )
+ return
;
}
-export { Badge, badgeVariants }
\ No newline at end of file
+export { Badge, badgeVariants };
diff --git a/src/components/ui/BlurFade.tsx b/src/components/ui/BlurFade.tsx
index 6312238..5003ce7 100644
--- a/src/components/ui/BlurFade.tsx
+++ b/src/components/ui/BlurFade.tsx
@@ -1,10 +1,14 @@
-"use client";
+'use client';
-import { AnimatePresence, motion, useInView, Variants } from "framer-motion";
-import { useRef } from "react";
+import { AnimatePresence, motion, useInView, Variants } from 'framer-motion';
+import { useRef } from 'react';
type MarginValue = `${number}${'px' | '%'}`;
-type MarginType = MarginValue | `${MarginValue} ${MarginValue}` | `${MarginValue} ${MarginValue} ${MarginValue}` | `${MarginValue} ${MarginValue} ${MarginValue} ${MarginValue}`;
+type MarginType =
+ | MarginValue
+ | `${MarginValue} ${MarginValue}`
+ | `${MarginValue} ${MarginValue} ${MarginValue}`
+ | `${MarginValue} ${MarginValue} ${MarginValue} ${MarginValue}`;
interface BlurFadeProps {
children: React.ReactNode;
@@ -28,15 +32,15 @@ const BlurFade = ({
delay = 0,
yOffset = 6,
inView = false,
- inViewMargin = "-50px",
- blur = "6px",
+ inViewMargin = '-50px',
+ blur = '6px',
}: BlurFadeProps) => {
const ref = useRef(null);
const inViewResult = useInView(ref, { once: true, margin: inViewMargin });
const isInView = !inView || inViewResult;
const defaultVariants: Variants = {
hidden: { y: yOffset, opacity: 0, filter: `blur(${blur})` },
- visible: { y: -yOffset, opacity: 1, filter: `blur(0px)` },
+ visible: { y: -yOffset, opacity: 1, filter: 'blur(0px)' },
};
const combinedVariants = variant || defaultVariants;
return (
@@ -44,13 +48,13 @@ const BlurFade = ({
diff --git a/src/components/ui/Highlighter.tsx b/src/components/ui/Highlighter.tsx
index 2aa2bfc..af02f99 100644
--- a/src/components/ui/Highlighter.tsx
+++ b/src/components/ui/Highlighter.tsx
@@ -1,8 +1,16 @@
-import { cn } from "@/lib/utils";
+import { cn } from '@/lib/utils';
interface HighlightProps {
- text: string
- color?: 'cyan' | 'pink' | 'violet-web' | 'slate-blue' | 'red' | 'yellow' | 'lemon' | 'laser-lemon'
+ text: string;
+ color?:
+ | 'cyan'
+ | 'pink'
+ | 'violet-web'
+ | 'slate-blue'
+ | 'red'
+ | 'yellow'
+ | 'lemon'
+ | 'laser-lemon';
}
/**
@@ -13,27 +21,21 @@ interface HighlightProps {
* @returns Returns a `span` element with the highlighted text of specified color.
*
*/
-export default function Highlight({ text, color = "yellow" }: HighlightProps) {
- let colorClass: string;
+export default function Highlight({ text, color = 'yellow' }: HighlightProps) {
+ let colorClass: string;
- if (color === "cyan") {
- colorClass = "bg-cyan-200 dark:bg-cyan-500/30";
- }
- else if (color === "pink" || color === "violet-web") {
- colorClass = "bg-[#ffa7ee] dark:bg-[#f73ed2]/30";
- }
- else if (color === "slate-blue") {
- colorClass = "bg-[#a0a8ff] dark:bg-[#675bf9]/50";
- }
- else if (color === "red") {
- colorClass = "bg-[#ffa0a0] dark:bg-[#f83b3b]/50";
- }
- else {
- // This covers "yellow", "lemon", "laser-lemon", and the default case
- colorClass = "bg-[#ffff77] dark:bg-[#fce913]/30";
- }
+ if (color === 'cyan') {
+ colorClass = 'bg-cyan-200 dark:bg-cyan-500/30';
+ } else if (color === 'pink' || color === 'violet-web') {
+ colorClass = 'bg-[#ffa7ee] dark:bg-[#f73ed2]/30';
+ } else if (color === 'slate-blue') {
+ colorClass = 'bg-[#a0a8ff] dark:bg-[#675bf9]/50';
+ } else if (color === 'red') {
+ colorClass = 'bg-[#ffa0a0] dark:bg-[#f83b3b]/50';
+ } else {
+ // This covers "yellow", "lemon", "laser-lemon", and the default case
+ colorClass = 'bg-[#ffff77] dark:bg-[#fce913]/30';
+ }
- return (
- {text}
- )
+ return {text} ;
}
diff --git a/src/components/ui/HoverHighlighter.tsx b/src/components/ui/HoverHighlighter.tsx
index ebc09f7..6465b40 100644
--- a/src/components/ui/HoverHighlighter.tsx
+++ b/src/components/ui/HoverHighlighter.tsx
@@ -1,8 +1,17 @@
-import { cn } from "@/lib/utils";
+import { cn } from '@/lib/utils';
interface HoverHighlightProps {
- text: string
- color?: 'cyan' | 'pink' | 'violet-web' | 'slate-blue' | 'red' | 'yellow' | 'lemon' | 'laser-lemon' | 'background'
+ text: string;
+ color?:
+ | 'cyan'
+ | 'pink'
+ | 'violet-web'
+ | 'slate-blue'
+ | 'red'
+ | 'yellow'
+ | 'lemon'
+ | 'laser-lemon'
+ | 'background';
}
/**
@@ -12,41 +21,40 @@ interface HoverHighlightProps {
* @param color - the color of the highlighter [cyan,pink,violet-web,slate-blue,red,yellow,lemon,laser-lemon,background]
*
*/
-export default function HoverHighlight({ text, color = "background" }: HoverHighlightProps) {
- let colorClass: string;
+export default function HoverHighlight({ text, color = 'background' }: HoverHighlightProps) {
+ let colorClass: string;
- if (color === "cyan") {
- colorClass = "bg-cyan-200 dark:bg-cyan-500/30";
- }
- else if (color === "pink" || color === "violet-web") {
- colorClass = "bg-[#ffa7ee] dark:bg-[#f73ed2]/30";
- }
- else if (color === "slate-blue") {
- colorClass = "bg-[#a0a8ff] dark:bg-[#675bf9]/50";
- }
- else if (color === "red") {
- colorClass = "bg-[#ffa0a0] dark:bg-[#f83b3b]/50";
- }
- else if (color === "background") {
- colorClass = "dark:bg-neutral-100 bg-neutral-950";
- }
- else {
- // This covers "yellow", "lemon", "laser-lemon", and the default case
- colorClass = "bg-[#ffff77] dark:bg-[#fce913]/30";
- }
+ if (color === 'cyan') {
+ colorClass = 'bg-cyan-200 dark:bg-cyan-500/30';
+ } else if (color === 'pink' || color === 'violet-web') {
+ colorClass = 'bg-[#ffa7ee] dark:bg-[#f73ed2]/30';
+ } else if (color === 'slate-blue') {
+ colorClass = 'bg-[#a0a8ff] dark:bg-[#675bf9]/50';
+ } else if (color === 'red') {
+ colorClass = 'bg-[#ffa0a0] dark:bg-[#f83b3b]/50';
+ } else if (color === 'background') {
+ colorClass = 'dark:bg-neutral-100 bg-neutral-950';
+ } else {
+ // This covers "yellow", "lemon", "laser-lemon", and the default case
+ colorClass = 'bg-[#ffff77] dark:bg-[#fce913]/30';
+ }
- return (
-
-
- {text}
-
- )
+ return (
+
+
+
+ {text}
+
+
+ );
}
diff --git a/src/components/ui/LinkWithArrow.tsx b/src/components/ui/LinkWithArrow.tsx
index 85e9f46..b63369f 100644
--- a/src/components/ui/LinkWithArrow.tsx
+++ b/src/components/ui/LinkWithArrow.tsx
@@ -1,24 +1,25 @@
-"use client";
+'use client';
-import { cn } from "@/lib/utils";
-import { ArrowUpRight } from 'lucide-react'
+import { ArrowUpRight } from 'lucide-react';
+import { cn } from '@/lib/utils';
-export default function LinkWithArrow({ className, href, children, ...props }: React.AnchorHTMLAttributes) {
+export default function LinkWithArrow({
+ className,
+ href,
+ children,
+ ...props
+}: React.AnchorHTMLAttributes) {
return (
-
- {children}
-
+
+ {children}
+
);
-}
\ No newline at end of file
+}
diff --git a/src/components/ui/Loader.tsx b/src/components/ui/Loader.tsx
index 1f74822..aea1352 100644
--- a/src/components/ui/Loader.tsx
+++ b/src/components/ui/Loader.tsx
@@ -1,16 +1,30 @@
export default function Loading() {
- return (
-
-
+ return (
+
+
+ d="M32 3C35.8083 3 39.5794 3.75011 43.0978 5.20749C46.6163 6.66488 49.8132 8.80101 52.5061 11.4939C55.199 14.1868 57.3351 17.3837 58.7925 20.9022C60.2499 24.4206 61 28.1917 61 32C61 35.8083 60.2499 39.5794 58.7925 43.0978C57.3351 46.6163 55.199 49.8132 52.5061 52.5061C49.8132 55.199 46.6163 57.3351 43.0978 58.7925C39.5794 60.2499 35.8083 61 32 61C28.1917 61 24.4206 60.2499 20.9022 58.7925C17.3837 57.3351 14.1868 55.199 11.4939 52.5061C8.801 49.8132 6.66487 46.6163 5.20749 43.0978C3.7501 39.5794 3 35.8083 3 32C3 28.1917 3.75011 24.4206 5.2075 20.9022C6.66489 17.3837 8.80101 14.1868 11.4939 11.4939C14.1868 8.80099 17.3838 6.66487 20.9022 5.20749C24.4206 3.7501 28.1917 3 32 3L32 3Z"
+ stroke="currentColor"
+ strokeWidth="5"
+ strokeLinecap="round"
+ strokeLinejoin="round"
+ >
-
-
+ d="M32 3C36.5778 3 41.0906 4.08374 45.1692 6.16256C49.2477 8.24138 52.7762 11.2562 55.466 14.9605C58.1558 18.6647 59.9304 22.9531 60.6448 27.4748C61.3591 31.9965 60.9928 36.6232 59.5759 40.9762"
+ stroke="currentColor"
+ strokeWidth="5"
+ strokeLinecap="round"
+ strokeLinejoin="round"
+ className="text-gray-900"
+ >
+
- )
+ );
}
diff --git a/src/data/resume.ts b/src/data/resume.ts
index f71cfee..0250a7c 100644
--- a/src/data/resume.ts
+++ b/src/data/resume.ts
@@ -1,117 +1,133 @@
export const detailsForMetadata = {
- baseUrl: "https://steadyfall.github.io/",
- title: "Himank Dave",
- name: "Himank Dave",
- ogImage: "/icon.ico",
- description: "A sleek, minimalistic and dynamic personal website built with React, TailwindCSS, Next.js and Framer Motion."
+ baseUrl: 'https://steadyfall.github.io/',
+ title: 'Himank Dave',
+ name: 'Himank Dave',
+ ogImage: '/icon.ico',
+ description:
+ 'A sleek, minimalistic and dynamic personal website built with React, TailwindCSS, Next.js and Framer Motion.',
};
export const personalDetails = {
- name: "Himank Dave",
- pronouns: "he/him/his",
- currentEducation: "Computational Mathematics [at] UWaterloo",
- currentJob: ["Software Developer in Test Intern", "Geotab"],
- basedFrom: "Toronto",
- githubLink: "https://github.com/steadyfall",
- linkedinLink: "https://www.linkedin.com/in/himank-dave/",
- email: "hddave@uwaterloo.ca",
- resumeFile: "resume.pdf",
-}
+ name: 'Himank Dave',
+ pronouns: 'he/him/his',
+ currentEducation: 'Computational Mathematics [at] UWaterloo',
+ currentJob: ['Software Developer in Test Intern', 'Geotab'],
+ basedFrom: 'Toronto',
+ githubLink: 'https://github.com/steadyfall',
+ linkedinLink: 'https://www.linkedin.com/in/himank-dave/',
+ email: 'hddave@uwaterloo.ca',
+ resumeFile: 'resume.pdf',
+};
export const experiences = [
- {
- companyLogo: "/images/geotab.jpeg",
- companyName: "Geotab",
- companyLink: "https://geotab.com/",
- position: "Test Automation Developer Intern",
- period: "Jan 2025 - Present",
- },
- {
- companyLogo: "/images/cactuscreatives.png",
- companyName: "Cactus Creatives",
- companyLink: "https://cactuscreatives.com/",
- position: "Software Developer Intern, Core",
- period: "May 2024 - August 2024",
- responsibilities: [
- `Developed an ETL pipeline for unstructured hierarchical data with Flask, supporting interactive visualizations via React and D3.js.`,
- `Built a self-hosted uptime monitoring tool using Node.js, Axios for web & database monitoring, Redis for data storage, and Socket.IO for real-time websocket communication, with VPS deployment via Docker.`,
- `Designed multiple CI/CD pipelines using Github Actions to automate unit and integration testing with Jest and Cypress, deployment, and monitoring processes for the uptime monitoring tool.`,
- `Engineered a domain-specific chatbot with 85% accuracy, leveraging a PDF-trained algorithm, custom model trainer, and OpenAI's NLP API for multilingual responses.`
- ]
- },
- {
- companyLogo: "/images/cactuscreatives.png",
- companyName: "Cactus Creatives",
- companyLink: "https://cactuscreatives.com/",
- position: "Python Developer Intern",
- period: "May 2023 - August 2023",
- responsibilities: [
- "Developed and maintained full-stack content management system in HTMX and Django, handling real-time metrics.",
- "Designed REST APIs with Flask and REST Framework in production with 1000+ requests/day.",
- "Implemented a Python script to parse and migrate over 25,000+ records from MySQL to PostgreSQL databases.",
- "Optimized SQL queries in the client codebase to align with updated database schema and improve performance post-migration.",
- "Analyzed large product usage datasets through linear/logistic regression and outlier detection, leading to over 25% client savings.",
- ]
- }
-]
+ {
+ companyLogo: '/images/geotab.jpeg',
+ companyName: 'Geotab',
+ companyLink: 'https://geotab.com/',
+ position: 'Test Automation Developer Intern',
+ period: 'Jan 2025 - Present',
+ },
+ {
+ companyLogo: '/images/cactuscreatives.png',
+ companyName: 'Cactus Creatives',
+ companyLink: 'https://cactuscreatives.com/',
+ position: 'Software Developer Intern, Core',
+ period: 'May 2024 - August 2024',
+ responsibilities: [
+ 'Developed an ETL pipeline for unstructured hierarchical data with Flask, supporting interactive visualizations via React and D3.js.',
+ 'Built a self-hosted uptime monitoring tool using Node.js, Axios for web & database monitoring, Redis for data storage, and Socket.IO for real-time websocket communication, with VPS deployment via Docker.',
+ 'Designed multiple CI/CD pipelines using Github Actions to automate unit and integration testing with Jest and Cypress, deployment, and monitoring processes for the uptime monitoring tool.',
+ "Engineered a domain-specific chatbot with 85% accuracy, leveraging a PDF-trained algorithm, custom model trainer, and OpenAI's NLP API for multilingual responses.",
+ ],
+ },
+ {
+ companyLogo: '/images/cactuscreatives.png',
+ companyName: 'Cactus Creatives',
+ companyLink: 'https://cactuscreatives.com/',
+ position: 'Python Developer Intern',
+ period: 'May 2023 - August 2023',
+ responsibilities: [
+ 'Developed and maintained full-stack content management system in HTMX and Django, handling real-time metrics.',
+ 'Designed REST APIs with Flask and REST Framework in production with 1000+ requests/day.',
+ 'Implemented a Python script to parse and migrate over 25,000+ records from MySQL to PostgreSQL databases.',
+ 'Optimized SQL queries in the client codebase to align with updated database schema and improve performance post-migration.',
+ 'Analyzed large product usage datasets through linear/logistic regression and outlier detection, leading to over 25% client savings.',
+ ],
+ },
+];
export const projects = [
- {
- title: "Trivivo",
- description: "Crafted a feature-rich, dynamic quiz game platform with progressive difficulty levels, real-time analytics, and comprehensive admin tools, complemented by a secure API for question management and user engagement tracking.",
- repo: true,
- repoUrl: "https://github.com/steadyfall/wwbm-webapp",
- live: false,
- techStack: [`Django`, `MySQL`, `HTML5`, `TailwindCSS`, `jQuery`, `Docker`, `AWS EC2`]
- },
- {
- title: "SpectraSVD",
- description: "Designed and deployed an advanced image compression algorithm achieving 25%+ size reduction, complemented by a real-time interactive web application for demonstrating compression quality and efficiency.",
- repo: true,
- repoUrl: "https://github.com/steadyfall/svd-compression",
- live: true,
- liveUrl: "https://spectrasvd.streamlit.app/",
- techStack: [`NumPy`, `OpenCV`, `Pillow`, `Streamlit`]
- },
- {
- title: "Personal Website",
- description: "Built a modern, responsive portfolio website with fluid animations, architected as a customizable template.",
- repo: true,
- repoUrl: "https://github.com/steadyfall/steadyfall.github.io.git",
- live: true,
- liveUrl: "https://steadyfall.github.io",
- techStack: [`TypeScript`, `React`, `TailwindCSS`, `Next.js`, `Framer Motion`,`Node.js`]
- },
- {
- title: "Chess (CS246 Final Project)",
- description: "Developed a C++ chess engine with innovative multi-player variants and AI capabilities, employing advanced software design patterns and rigorous testing methodologies in a Linux environment.",
- repo: false,
- live: false,
- techStack: [`C++`, `CMake`, `XQuartz`]
- },
- {
- title: "EcoWiz",
- description: "Engineered a full-stack application featuring a multimodal image classification system for garbage sorting, achieving 80% accuracy through advanced deep learning techniques.",
- repo: true,
- repoUrl: "https://github.com/karman103/DeltaHacksX",
- live: false,
- techStack: [`React`, `Flask`, `ResNet-50 CNN`, `YOLOv8`]
- },
- {
- title: "RedWish",
- description: "Developed a comprehensive full-stack health application to streamline blood donation and transfusion processes, incorporating an AI-powered chatbot to enhance user engagement and accessibility.",
- repo: true,
- repoUrl: "https://github.com/steadyfall/RedWish",
- live: true,
- liveUrl: "https://steadyfall.github.io/RedWish/",
- techStack: [`HTML5`, `CSS3`, `JavaScript`, `Firebase`, `DialogFlow API`]
- },
-]
+ {
+ title: 'Trivivo',
+ description:
+ 'Crafted a feature-rich, dynamic quiz game platform with progressive difficulty levels, real-time analytics, and comprehensive admin tools, complemented by a secure API for question management and user engagement tracking.',
+ repo: true,
+ repoUrl: 'https://github.com/steadyfall/wwbm-webapp',
+ live: false,
+ techStack: ['Django', 'MySQL', 'HTML5', 'TailwindCSS', 'jQuery', 'Docker', 'AWS EC2'],
+ },
+ {
+ title: 'SpectraSVD',
+ description:
+ 'Designed and deployed an advanced image compression algorithm achieving 25%+ size reduction, complemented by a real-time interactive web application for demonstrating compression quality and efficiency.',
+ repo: true,
+ repoUrl: 'https://github.com/steadyfall/svd-compression',
+ live: true,
+ liveUrl: 'https://spectrasvd.streamlit.app/',
+ techStack: ['NumPy', 'OpenCV', 'Pillow', 'Streamlit'],
+ },
+ {
+ title: 'Personal Website',
+ description:
+ 'Built a modern, responsive portfolio website with fluid animations, architected as a customizable template.',
+ repo: true,
+ repoUrl: 'https://github.com/steadyfall/steadyfall.github.io.git',
+ live: true,
+ liveUrl: 'https://steadyfall.github.io',
+ techStack: ['TypeScript', 'React', 'TailwindCSS', 'Next.js', 'Framer Motion', 'Node.js'],
+ },
+ {
+ title: 'Chess (CS246 Final Project)',
+ description:
+ 'Developed a C++ chess engine with innovative multi-player variants and AI capabilities, employing advanced software design patterns and rigorous testing methodologies in a Linux environment.',
+ repo: false,
+ live: false,
+ techStack: ['C++', 'CMake', 'XQuartz'],
+ },
+ {
+ title: 'EcoWiz',
+ description:
+ 'Engineered a full-stack application featuring a multimodal image classification system for garbage sorting, achieving 80% accuracy through advanced deep learning techniques.',
+ repo: true,
+ repoUrl: 'https://github.com/karman103/DeltaHacksX',
+ live: false,
+ techStack: ['React', 'Flask', 'ResNet-50 CNN', 'YOLOv8'],
+ },
+ {
+ title: 'RedWish',
+ description:
+ 'Developed a comprehensive full-stack health application to streamline blood donation and transfusion processes, incorporating an AI-powered chatbot to enhance user engagement and accessibility.',
+ repo: true,
+ repoUrl: 'https://github.com/steadyfall/RedWish',
+ live: true,
+ liveUrl: 'https://steadyfall.github.io/RedWish/',
+ techStack: ['HTML5', 'CSS3', 'JavaScript', 'Firebase', 'DialogFlow API'],
+ },
+];
export const skills = {
- languages: ["Python", "JavaScript (ES6)", "TypeScript", "Go", "C", "C++20", "SQL"],
- frameworksAndLibraries: ["Django", "Flask", "React", "TailwindCSS", "Next.js", "Node.js", "Postgres", "MySQL"],
- tools: ["Bash", "Powershell", "Docker", "Postman"],
-}
+ languages: ['Python', 'JavaScript (ES6)', 'TypeScript', 'Go', 'C', 'C++20', 'SQL'],
+ frameworksAndLibraries: [
+ 'Django',
+ 'Flask',
+ 'React',
+ 'TailwindCSS',
+ 'Next.js',
+ 'Node.js',
+ 'Postgres',
+ 'MySQL',
+ ],
+ tools: ['Bash', 'Powershell', 'Docker', 'Postman'],
+};
// "Axios", "Socket.IO"
// databases: ["Postgres", "MySQL", "Redis"]
diff --git a/src/hooks/useMediaQuery.tsx b/src/hooks/useMediaQuery.tsx
index 64ce845..7f5509b 100644
--- a/src/hooks/useMediaQuery.tsx
+++ b/src/hooks/useMediaQuery.tsx
@@ -1,19 +1,19 @@
-'use client'
+'use client';
-import { useState, useEffect } from 'react'
+import { useState, useEffect } from 'react';
export function useMediaQuery(query: string): boolean {
- const [matches, setMatches] = useState(false)
+ const [matches, setMatches] = useState(false);
useEffect(() => {
- const media = window.matchMedia(query)
+ const media = window.matchMedia(query);
if (media.matches !== matches) {
- setMatches(media.matches)
+ setMatches(media.matches);
}
- const listener = () => setMatches(media.matches)
- window.addEventListener('resize', listener)
- return () => window.removeEventListener('resize', listener)
- }, [matches, query])
+ const listener = () => setMatches(media.matches);
+ window.addEventListener('resize', listener);
+ return () => window.removeEventListener('resize', listener);
+ }, [matches, query]);
- return matches
+ return matches;
}
diff --git a/src/hooks/useReadingProgress.tsx b/src/hooks/useReadingProgress.tsx
index 70b8555..4b6a0fc 100644
--- a/src/hooks/useReadingProgress.tsx
+++ b/src/hooks/useReadingProgress.tsx
@@ -1,25 +1,23 @@
-import { useEffect, useState } from 'react'
+import { useEffect, useState } from 'react';
const useReadingProgress = () => {
- const [completion, setCompletion] = useState(0);
+ const [completion, setCompletion] = useState(0);
- useEffect(() => {
- const updateScrollCompletion = () => {
- const currentProgress = window.scrollY;
- const scrollHeight = document.body.scrollHeight - window.innerHeight;
- if (scrollHeight) {
- setCompletion(
- Number((currentProgress / scrollHeight).toFixed(2)) * 100
- );
- }
- }
- window.addEventListener('scroll', updateScrollCompletion);
- return () => {
- window.removeEventListener('scroll', updateScrollCompletion);
- }
- }, []);
+ useEffect(() => {
+ const updateScrollCompletion = () => {
+ const currentProgress = window.scrollY;
+ const scrollHeight = document.body.scrollHeight - window.innerHeight;
+ if (scrollHeight) {
+ setCompletion(Number((currentProgress / scrollHeight).toFixed(2)) * 100);
+ }
+ };
+ window.addEventListener('scroll', updateScrollCompletion);
+ return () => {
+ window.removeEventListener('scroll', updateScrollCompletion);
+ };
+ }, []);
- return completion;
-}
+ return completion;
+};
-export default useReadingProgress
\ No newline at end of file
+export default useReadingProgress;
diff --git a/src/hooks/useSmoothScroll.tsx b/src/hooks/useSmoothScroll.tsx
index 338afdb..e527634 100644
--- a/src/hooks/useSmoothScroll.tsx
+++ b/src/hooks/useSmoothScroll.tsx
@@ -1,48 +1,51 @@
-'use client'
+'use client';
-import { useEffect, useCallback } from 'react'
-import { useRouter, usePathname } from 'next/navigation'
+import { useRouter, usePathname } from 'next/navigation';
+import { useEffect, useCallback } from 'react';
export function useSmoothScroll(offset = 0) {
- const router = useRouter()
- const pathname = usePathname()
-
- const scrollToElement = useCallback((hash: string) => {
- const targetElement = document.getElementById(hash.substring(1))
- if (targetElement) {
- const yPosition = targetElement.getBoundingClientRect().top + window.pageYOffset - offset
- window.scrollTo({ top: yPosition, behavior: 'smooth' })
- }
- }, [offset])
+ const router = useRouter();
+ const pathname = usePathname();
+
+ const scrollToElement = useCallback(
+ (hash: string) => {
+ const targetElement = document.getElementById(hash.substring(1));
+ if (targetElement) {
+ const yPosition = targetElement.getBoundingClientRect().top + window.pageYOffset - offset;
+ window.scrollTo({ top: yPosition, behavior: 'smooth' });
+ }
+ },
+ [offset],
+ );
useEffect(() => {
if (window.location.hash) {
- scrollToElement(window.location.hash)
+ scrollToElement(window.location.hash);
}
- }, [pathname, scrollToElement])
+ }, [pathname, scrollToElement]);
useEffect(() => {
const handleClick = (e: MouseEvent) => {
- const target = e.target as HTMLElement
- const anchor = target.closest('a')
- if (!anchor) return
+ const target = e.target as HTMLElement;
+ const anchor = target.closest('a');
+ if (!anchor) return;
- const href = anchor.getAttribute('href')
- if (!href) return
+ const href = anchor.getAttribute('href');
+ if (!href) return;
- const url = new URL(href, window.location.origin)
+ const url = new URL(href, window.location.origin);
if (url.pathname === pathname && url.hash) {
- e.preventDefault()
- scrollToElement(url.hash)
- window.history.pushState(null, '', url.hash)
+ e.preventDefault();
+ scrollToElement(url.hash);
+ window.history.pushState(null, '', url.hash);
} else if (url.pathname !== pathname && url.hash) {
- e.preventDefault()
- router.push(href)
+ e.preventDefault();
+ router.push(href);
}
- }
+ };
- document.addEventListener('click', handleClick)
- return () => document.removeEventListener('click', handleClick)
- }, [pathname, router, scrollToElement])
-}
\ No newline at end of file
+ document.addEventListener('click', handleClick);
+ return () => document.removeEventListener('click', handleClick);
+ }, [pathname, router, scrollToElement]);
+}
diff --git a/src/lib/posts.ts b/src/lib/posts.ts
index 193b3bf..558dd17 100644
--- a/src/lib/posts.ts
+++ b/src/lib/posts.ts
@@ -1,45 +1,44 @@
export type PostMetadata = {
- title?: string
- description?: string
- date?: string
- tags?: string[]
- slug: string
-}
+ title?: string;
+ description?: string;
+ date?: string;
+ tags?: string[];
+ slug: string;
+};
export type PostData = {
- metadata: PostMetadata,
- content: string
-}
+ metadata: PostMetadata;
+ content: string;
+};
+
+export function groupPostsByYear(posts: PostMetadata[]): { year: number; posts: PostMetadata[] }[] {
+ // Filter out posts with no date to avoid runtime errors
+ // Sort posts by date in descending order
+ // Group posts by year
+ // Sort by year in descending order
-export function groupPostsByYear(
- posts: PostMetadata[]
-): { year: number; posts: PostMetadata[] }[] {
- // Filter out posts with no date to avoid runtime errors
- // Sort posts by date in descending order
- // Group posts by year
- // Sort by year in descending order
-
- posts.sort(
- (a, b) =>
- (new Date(b.date ?? "").getTime() || 0) -
- (new Date(a.date ?? "").getTime() || 0),
- )
+ posts.sort(
+ (a, b) => (new Date(b.date ?? '').getTime() || 0) - (new Date(a.date ?? '').getTime() || 0),
+ );
- const groupedByYear: { [key: number]: PostMetadata[] } = posts.reduce((acc, post) => {
- const year = new Date(post.date ?? "").getFullYear() || 1
- if (!acc[year]) {
- acc[year] = [];
- }
- acc[year].push(post);
- return acc;
- }, {} as { [key: number]: PostMetadata[] });
+ const groupedByYear: { [key: number]: PostMetadata[] } = posts.reduce(
+ (acc, post) => {
+ const year = new Date(post.date ?? '').getFullYear() || 1;
+ if (!acc[year]) {
+ acc[year] = [];
+ }
+ acc[year].push(post);
+ return acc;
+ },
+ {} as { [key: number]: PostMetadata[] },
+ );
- const postsByYear = Object.keys(groupedByYear)
- .map(year => ({
- year: parseInt(year),
- posts: groupedByYear[parseInt(year)]
- }))
- .sort((a, b) => b.year - a.year);
+ const postsByYear = Object.keys(groupedByYear)
+ .map((year) => ({
+ year: parseInt(year),
+ posts: groupedByYear[parseInt(year)],
+ }))
+ .sort((a, b) => b.year - a.year);
- return postsByYear;
+ return postsByYear;
}
diff --git a/src/lib/techIcons.ts b/src/lib/techIcons.ts
index 58bd095..54189e2 100644
--- a/src/lib/techIcons.ts
+++ b/src/lib/techIcons.ts
@@ -1,46 +1,46 @@
export const techIcons: { [key: string]: string } = {
- "AWS": "la:aws",
- "AWS EC2": "simple-icons:amazonec2",
- "Axios": "simple-icons:axios",
- "Bash": "devicon-plain:bash",
- "C": "devicon:c",
- "C++": "devicon:cplusplus",
- "C++20": "devicon:cplusplus",
- "Cloudflare": "devicon:cloudflare",
- "CMake": "devicon:cmake",
- "CSS3": "devicon:css3",
- "Cypress": "devicon-plain:cypressio",
- "D3.js": "logos:d3",
- "Django": "vscode-icons:file-type-django",
- "Docker": "logos:docker-icon",
- "Firebase": "devicon:firebase",
- "Flask": "simple-icons:flask",
- "Framer Motion": "simple-icons:framer",
- "Go": "logos:go",
- "HTML5": "devicon:html5",
- "JavaScript": "devicon:javascript",
- "JavaScript (ES6)": "devicon:javascript",
- "Jest": "logos:jest",
- "jQuery": "devicon-plain:jquery",
- "Linux": "devicon:linux",
- "MongoDB": "logos:mongodb-icon",
- "MySQL": "fontisto:mysql",
- "Next.js": "devicon:nextjs",
- "Node.js": "devicon:nodejs",
- "NumPy": "devicon:numpy",
- "OpenCV": "devicon:opencv",
- "Postgres": "logos:postgresql",
- "Postman": "devicon:postman",
- "Powershell": "vscode-icons:file-type-powershell",
- "Pytest": "simple-icons:pytest",
- "Python": "devicon:python",
- "Python3": "devicon:python",
- "R": "devicon:rstudio",
- "Railway": "simple-icons:railway",
- "React": "logos:react",
- "Redis": "devicon:redis",
- "Socket.IO": "simple-icons:socketdotio",
- "Streamlit": "devicon:streamlit",
- "TailwindCSS": "devicon:tailwindcss",
- "TypeScript": "devicon:typescript",
-}
+ AWS: 'la:aws',
+ 'AWS EC2': 'simple-icons:amazonec2',
+ Axios: 'simple-icons:axios',
+ Bash: 'devicon-plain:bash',
+ C: 'devicon:c',
+ 'C++': 'devicon:cplusplus',
+ 'C++20': 'devicon:cplusplus',
+ Cloudflare: 'devicon:cloudflare',
+ CMake: 'devicon:cmake',
+ CSS3: 'devicon:css3',
+ Cypress: 'devicon-plain:cypressio',
+ 'D3.js': 'logos:d3',
+ Django: 'vscode-icons:file-type-django',
+ Docker: 'logos:docker-icon',
+ Firebase: 'devicon:firebase',
+ Flask: 'simple-icons:flask',
+ 'Framer Motion': 'simple-icons:framer',
+ Go: 'logos:go',
+ HTML5: 'devicon:html5',
+ JavaScript: 'devicon:javascript',
+ 'JavaScript (ES6)': 'devicon:javascript',
+ Jest: 'logos:jest',
+ jQuery: 'devicon-plain:jquery',
+ Linux: 'devicon:linux',
+ MongoDB: 'logos:mongodb-icon',
+ MySQL: 'fontisto:mysql',
+ 'Next.js': 'devicon:nextjs',
+ 'Node.js': 'devicon:nodejs',
+ NumPy: 'devicon:numpy',
+ OpenCV: 'devicon:opencv',
+ Postgres: 'logos:postgresql',
+ Postman: 'devicon:postman',
+ Powershell: 'vscode-icons:file-type-powershell',
+ Pytest: 'simple-icons:pytest',
+ Python: 'devicon:python',
+ Python3: 'devicon:python',
+ R: 'devicon:rstudio',
+ Railway: 'simple-icons:railway',
+ React: 'logos:react',
+ Redis: 'devicon:redis',
+ 'Socket.IO': 'simple-icons:socketdotio',
+ Streamlit: 'devicon:streamlit',
+ TailwindCSS: 'devicon:tailwindcss',
+ TypeScript: 'devicon:typescript',
+};
diff --git a/src/lib/utils.ts b/src/lib/utils.ts
index ff36dd4..104214f 100644
--- a/src/lib/utils.ts
+++ b/src/lib/utils.ts
@@ -1,5 +1,5 @@
-import { type ClassValue, clsx } from "clsx";
-import { twMerge } from "tailwind-merge";
+import { type ClassValue, clsx } from 'clsx';
+import { twMerge } from 'tailwind-merge';
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
@@ -9,7 +9,7 @@ export const formatDate = (d: string): string => {
return new Intl.DateTimeFormat('en-US', {
month: 'short',
day: '2-digit',
- year: 'numeric'
+ year: 'numeric',
}).format(new Date(d));
};
diff --git a/tailwind.config.ts b/tailwind.config.ts
index 531a545..4385a4e 100644
--- a/tailwind.config.ts
+++ b/tailwind.config.ts
@@ -1,21 +1,21 @@
-import type { Config } from "tailwindcss";
+import type { Config } from 'tailwindcss';
const config: Config = {
content: [
- "./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
- "./src/components/**/*.{js,ts,jsx,tsx,mdx}",
- "./src/app/**/*.{js,ts,jsx,tsx,mdx}",
+ './src/pages/**/*.{js,ts,jsx,tsx,mdx}',
+ './src/components/**/*.{js,ts,jsx,tsx,mdx}',
+ './src/app/**/*.{js,ts,jsx,tsx,mdx}',
],
theme: {
extend: {
fontFamily: {
sans: ['var(--font-inter)'],
typewriter: ['var(--font-spaceMono)'],
- 'section': ['Starlight', 'sans-serif'],
+ section: ['Starlight', 'sans-serif'],
},
colors: {
- background: "var(--background)",
- foreground: "var(--foreground)",
+ background: 'var(--background)',
+ foreground: 'var(--foreground)',
'midnight-blue': {
'50': '#ebfaff',
'100': '#d3f3ff',
@@ -29,7 +29,7 @@ const config: Config = {
'900': '#0452a4',
'950': '#0a3b76',
},
- 'mantis': {
+ mantis: {
'50': '#f2fbf2',
'100': '#e2f7e1',
'200': '#c4eec4',
@@ -42,7 +42,7 @@ const config: Config = {
'900': '#1d4a1f',
'950': '#0b280c',
},
- 'sushi': {
+ sushi: {
'50': '#f4f9ec',
'100': '#e5f1d6',
'200': '#cee4b2',
@@ -94,7 +94,7 @@ const config: Config = {
'900': '#1d478b',
'950': '#162d55',
},
- 'thunderbird': {
+ thunderbird: {
'50': '#fff2f1',
'100': '#ffe1e0',
'200': '#ffc8c6',
@@ -107,7 +107,7 @@ const config: Config = {
'900': '#861a16',
'950': '#490906',
},
- 'starship': {
+ starship: {
'50': '#fbfde9',
'100': '#f6fcc5',
'200': '#f1f98f',
@@ -133,7 +133,7 @@ const config: Config = {
'900': '#6e2d43',
'950': '#3d1421',
},
- 'firefly': {
+ firefly: {
'50': '#f1f9fa',
'100': '#dceff1',
'200': '#bedfe3',
diff --git a/tsconfig.json b/tsconfig.json
index f48e7ee..51d0dbc 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,10 +1,6 @@
{
"compilerOptions": {
- "lib": [
- "dom",
- "dom.iterable",
- "esnext"
- ],
+ "lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
@@ -22,19 +18,10 @@
}
],
"paths": {
- "@/*": [
- "./src/*"
- ]
+ "@/*": ["./src/*"]
},
"target": "ES2017"
},
- "include": [
- "next-env.d.ts",
- "**/*.ts",
- "**/*.tsx",
- ".next/types/**/*.ts"
- ],
- "exclude": [
- "node_modules"
- ]
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
+ "exclude": ["node_modules"]
}