diff --git a/.gitignore b/.gitignore index c3e08f3..9df2c16 100644 --- a/.gitignore +++ b/.gitignore @@ -49,4 +49,7 @@ pnpm-lock.yaml # auto-generated icon files /icons/types -/public/icons/sprites \ No newline at end of file +/public/icons/sprites + +# Service worker +/public/sw.js \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 67e6533..ff53906 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "thebatproject", - "version": "0.1.1", + "version": "0.1.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "thebatproject", - "version": "0.1.1", + "version": "0.1.2", "dependencies": { "@tsparticles/all": "^3.7.1", "@tsparticles/engine": "^3.7.1", diff --git a/package.json b/package.json index 72578c4..ce0cf30 100644 --- a/package.json +++ b/package.json @@ -1,18 +1,20 @@ { "name": "thebatproject", - "version": "0.1.1", + "version": "0.1.2", "private": true, "type": "module", "scripts": { - "dev": "npm run pretty:check && npm run lint && npm run minify:images && npm run build:icons && next dev --turbopack", - "build": "npm run pretty:check && npm run lint && npm run minify:images && npm run build:icons && next build", + "dev": "npm run prebuild && next dev --turbopack", + "build": "npm run prebuild && next build", "start": "next start", "lint": "next lint --max-warnings 0", "pretty": "prettier --write \"./**/*.{json,tsx,ts,css,js,html,scss,yml}\"", "pretty:check": "prettier \"./**/*.{json,tsx,ts,css,js,html,scss,yml}\" --check", "minify:images": "npx tsx ./scripts/minify-images.ts", "build:ci": "npx @cloudflare/next-on-pages@1", - "build:icons": "npx tsx ./scripts/build-icons.ts" + "build:icons": "npx tsx ./scripts/build-icons.ts", + "generate:sw": "npx tsx ./scripts/generate-sw.ts", + "prebuild": "npm run pretty:check && npm run lint && npm run minify:images && npm run build:icons && npm run generate:sw" }, "dependencies": { "@tsparticles/all": "^3.7.1", diff --git a/scripts/generate-sw.ts b/scripts/generate-sw.ts new file mode 100644 index 0000000..403b2da --- /dev/null +++ b/scripts/generate-sw.ts @@ -0,0 +1,77 @@ +import pkg from "../package.json"; +import * as path from "node:path"; +import fsExtra from "fs-extra"; + +console.log(`Generating service worker for ${pkg.version}`); + +const scriptContent = ` +const cacheName = "${pkg.version}"; +const cacheLimit = 100; + +const ignoreEndPoints = []; + +const shouldIgnoreCaching = (request) => { + if (request.method !== "GET") { + return true; + } + return ignoreEndPoints.some((endPoint) => request.url.startsWith(endPoint)); +}; + +const cacheClone = async (request) => { + const res = await fetch(request); + const cache = await caches.open(cacheName); + await cache.put(request, res.clone()); + limitCacheSize(cacheName, cacheLimit); + return res; +}; + +const getCache = async (request) => { + const cache = await caches.match(request); + if (cache) { + return cache; + } + return cacheClone(request); +}; + +const clearOldCache = async () => { + const existingCaches = await caches.keys(); + return Promise.all( + existingCaches + .filter((cache) => cache !== cacheName) + .map((cache) => caches.delete(cache)), + ); +}; + +const limitCacheSize = async (cacheName, size) => { + const cache = await caches.open(cacheName); + const cacheKeys = await cache.keys(); + if (cacheKeys.length > size) { + await cache.delete(cacheKeys[0]); + limitCacheSize(cacheName, size); + } +}; + +self.addEventListener("install", (e) => { + self.skipWaiting(); + e.waitUntil(clearOldCache()); +}); + +self.addEventListener("activate", (e) => { + e.waitUntil(clearOldCache()); +}); + +self.addEventListener("fetch", (e) => { + if (shouldIgnoreCaching(e.request)) { + return; + } + e.respondWith(getCache(e.request)); +}); +`; + +const cwd = process.cwd(); +const outputDir = path.join(cwd, "public"); +const outputPath = path.join(outputDir, "sw.js"); +await fsExtra.ensureDir(outputDir); +await fsExtra.writeFile(outputPath, scriptContent, "utf8"); + +console.log(`Service worker generated at ${path.relative(cwd, outputPath)}`); diff --git a/src/app/_components/HeroCopy.tsx b/src/app/_components/HeroCopy.tsx index dae85e1..99a9c92 100644 --- a/src/app/_components/HeroCopy.tsx +++ b/src/app/_components/HeroCopy.tsx @@ -5,10 +5,10 @@ import { ExternalLinks } from "@/utils/constants"; export const HeroCopy = () => { return (
-

+

Developer by day,
Vigilante by night

-

+

“If I can't change things here,{" "}
if I can't have an effect,
I @@ -18,23 +18,23 @@ export const HeroCopy = () => {

SUMMON THE KNIGHT - + (Schedule a call) VIGILANTE PROFILE - + (Download resume) diff --git a/src/app/_components/HeroSection.tsx b/src/app/_components/HeroSection.tsx index 215ee92..8af4850 100644 --- a/src/app/_components/HeroSection.tsx +++ b/src/app/_components/HeroSection.tsx @@ -17,7 +17,9 @@ export const HeroSection = () => { className="w-full h-svh grid grid-cols-1 overflow-x-clip pt-32 xs:pt-32 sm:pt-36 lg:pt-16 lg:grid-cols-[1fr_1.2fr] items-center gap-10 xl:gap-20" > - +
+ +
); }; diff --git a/src/app/layout.tsx b/src/app/layout.tsx index fc182ec..2d1433c 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,6 +1,7 @@ import { Bebas_Neue, Inter } from "next/font/google"; import "@/styles/globals.css"; import { getMetaData, getViewPort } from "@/utils/helpers"; +import { ServiceWorker } from "@/components/ServiceWorker"; const rethinkSans = Inter({ subsets: ["latin"], @@ -25,9 +26,10 @@ export default function RootLayout({ return ( {children} + ); diff --git a/src/app/not-found.tsx b/src/app/not-found.tsx index fa24d8b..aee1562 100644 --- a/src/app/not-found.tsx +++ b/src/app/not-found.tsx @@ -7,15 +7,11 @@ const NotFound = () => {

Holy Broken Links, Batman!

-

+

Even Batman can't find this page... It might have vanished into the Batcave or been taken down by the Joker. Either way, it's not here.{" "}

- + Back to Home diff --git a/src/components/Button.tsx b/src/components/Button.tsx index f897c79..494d32c 100644 --- a/src/components/Button.tsx +++ b/src/components/Button.tsx @@ -5,7 +5,7 @@ export type ButtonVariant = "primary"; export const variantClasses: Record = { primary: - "border border-gray-500 rounded-md border-solid text-white/80 bg-black/70 backdrop-blur-sm py-3 px-5 focus-visible:[&:not(:disabled)]:text-white hover:[&:not(:disabled)]:border-white/50 hover:[&:not(:disabled)]:text-white disabled:opacity-30", + "border border-stroke rounded-md border-solid text-secondary bg-surface/70 backdrop-blur-sm py-3 px-5 focus-visible:[&:not(:disabled)]:text-primary hover:[&:not(:disabled)]:border-tertiary hover:[&:not(:disabled)]:text-primary disabled:opacity-30", }; export interface ButtonProps diff --git a/src/components/CustomLink.tsx b/src/components/CustomLink.tsx index fd7ca78..cad5f9c 100644 --- a/src/components/CustomLink.tsx +++ b/src/components/CustomLink.tsx @@ -16,7 +16,7 @@ export const CustomLink: React.ForwardRefExoticComponent< return ( { return (