diff --git a/src/App.tsx b/src/App.tsx
index 0358a9d8..7b08b20a 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -1,11 +1,17 @@
-import { Downasaur, Home as HomeIcon } from "@nsmr/pixelart-react";
+import {
+ Downasaur,
+ Github,
+ Home as HomeIcon,
+ Mail,
+ Notes,
+} from "@nsmr/pixelart-react";
import { useEffect, useState } from "react";
import { Link, Route, Routes, useParams } from "react-router-dom";
import { AskAI } from "@/components/ask-ai";
import { ProjectListItem } from "@/components/ProjectListItem";
+import { TimeSince } from "@/components/time-since";
import { type ProjectEntry, projectList, projects } from "@/projects";
import { ThemeToggle } from "./components/theme-toggle";
-import { Github, Mail, Notes } from "@nsmr/pixelart-react";
const asciiFiles = [
"cat-sleep.txt",
@@ -57,7 +63,7 @@ function Home() {
return (
-
+
{asciiArt ? (
{asciiArt}
@@ -92,6 +98,9 @@ function Home() {
CV
+
+ Age:
+
{sortedProjects.map((project) => (
diff --git a/src/components/time-since.tsx b/src/components/time-since.tsx
new file mode 100644
index 00000000..43075d7e
--- /dev/null
+++ b/src/components/time-since.tsx
@@ -0,0 +1,44 @@
+import { useEffect, useMemo, useState } from "react";
+import { cn } from "@/lib/utils";
+
+type TimeSinceProps = {
+ date: Date;
+ className?: string;
+ yearsDp?: number;
+};
+
+const yearMs = 1000 * 60 * 60 * 24 * 365.25;
+
+function roundToDp(value: number, dp: number) {
+ const factor = 10 ** dp;
+ return Math.floor(value * factor) / factor;
+}
+
+export function TimeSince({ date, className, yearsDp = 2 }: TimeSinceProps) {
+ const dateMs = useMemo(() => date.getTime(), [date]);
+ const [milliseconds, setMilliseconds] = useState(() =>
+ Math.max(0, Date.now() - dateMs),
+ );
+
+ useEffect(() => {
+ let rafId: number | null = null;
+
+ const tick = () => {
+ setMilliseconds(Math.max(0, Date.now() - dateMs));
+ rafId = requestAnimationFrame(tick);
+ };
+
+ rafId = requestAnimationFrame(tick);
+ return () => {
+ if (rafId !== null) cancelAnimationFrame(rafId);
+ };
+ }, [dateMs]);
+
+ const years = roundToDp(milliseconds / yearMs, yearsDp);
+
+ return (
+
+ {years}y or {milliseconds}ms
+
+ );
+}