From bb257b50c410d17c9e2d469ed32f598a5149589c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20M=20J=20Barata=20Ribeiro?= <122732773+Barata-Ribeiro@users.noreply.github.com> Date: Thu, 23 Jul 2026 07:26:02 -0300 Subject: [PATCH 1/4] feat(sw): add StaleWhileRevalidate handler for JSON caching and improve service worker configuration --- app/sw.ts | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/app/sw.ts b/app/sw.ts index 02f423f..0cc00c4 100644 --- a/app/sw.ts +++ b/app/sw.ts @@ -1,5 +1,5 @@ import type { PrecacheEntry, SerwistGlobalConfig } from 'serwist'; -import { CacheFirst, ExpirationPlugin, NetworkFirst, Serwist } from 'serwist'; +import { CacheFirst, ExpirationPlugin, NetworkFirst, Serwist, StaleWhileRevalidate } from 'serwist'; // This declares the value of `injectionPoint` to TypeScript. // `injectionPoint` is the string that will be replaced by the @@ -12,30 +12,43 @@ declare global { } declare const self: ServiceWorkerGlobalScope; +const isDev = process.env['NODE_ENV'] !== 'production'; const serwist = new Serwist({ precacheEntries: self.__SW_MANIFEST, + precacheOptions: { cleanupOutdatedCaches: true }, skipWaiting: true, clientsClaim: true, navigationPreload: true, + disableDevLogs: !isDev, runtimeCaching: [ { matcher: ({ request }) => request.mode === 'navigate', - handler: new NetworkFirst({ cacheName: 'pages', }), }, - { matcher: ({ request }) => request.destination === 'image', - handler: new CacheFirst({ cacheName: 'images', plugins: [ new ExpirationPlugin({ maxEntries: 200, - maxAgeSeconds: 60 * 60 * 24 * 30, + maxAgeSeconds: 60 * 60 * 24 * 30, // 30 days + }), + ], + }), + }, + { + matcher: ({ request }) => /\.json$/i.test(request.url) || request.url.endsWith('.json'), + handler: new StaleWhileRevalidate({ + cacheName: 'public-json', + plugins: [ + new ExpirationPlugin({ + maxEntries: 128, + maxAgeSeconds: 30 * 24 * 60 * 60, // 30 days + maxAgeFrom: 'last-used', }), ], }), From 620608f4d6ffc6282c79ea6cebdc3f438ff96935 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20M=20J=20Barata=20Ribeiro?= <122732773+Barata-Ribeiro@users.noreply.github.com> Date: Thu, 23 Jul 2026 07:26:17 -0300 Subject: [PATCH 2/4] feat(sw): enhance service worker with dev-only event listeners and dynamic route handling --- app/sw.ts | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/app/sw.ts b/app/sw.ts index 0cc00c4..7ef19d0 100644 --- a/app/sw.ts +++ b/app/sw.ts @@ -56,4 +56,31 @@ const serwist = new Serwist({ ], }); +if (isDev) { + self.addEventListener('install', (event) => { + console.log('Event install (dev only)', event); + self.skipWaiting(); + }); + + self.addEventListener('activate', (event) => { + event.waitUntil(self.clients.claim()); + }); + + self.addEventListener('fetch', (event) => console.log('Fetch event (dev only)', event)); +} + +self.addEventListener('install', (event) => { + event.waitUntil( + (async () => { + const routes: string[] = await fetch('/routes.json').then((r) => r.json()); + + const requestPromises = routes.map((route) => + Promise.resolve(serwist.handleRequest({ request: new Request(route), event })), + ); + + await Promise.allSettled(requestPromises); + })(), + ); +}); + serwist.addEventListeners(); From f938a56269c55bc4068d717e8f55fe6e5b2df6df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20M=20J=20Barata=20Ribeiro?= <122732773+Barata-Ribeiro@users.noreply.github.com> Date: Thu, 23 Jul 2026 14:56:08 -0300 Subject: [PATCH 3/4] feat(nav): add prefetching to NavLink components for improved performance --- app/components/navigation/nav-main.tsx | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/app/components/navigation/nav-main.tsx b/app/components/navigation/nav-main.tsx index ca916ca..2a20ada 100644 --- a/app/components/navigation/nav-main.tsx +++ b/app/components/navigation/nav-main.tsx @@ -88,7 +88,7 @@ export function NavMain() { className="cursor-pointer data-current:bg-sidebar-accent data-current:text-sidebar-accent-foreground" asChild > - + Home @@ -100,7 +100,7 @@ export function NavMain() { className="cursor-pointer data-current:bg-sidebar-accent data-current:text-sidebar-accent-foreground" asChild > - + About @@ -135,7 +135,12 @@ export function NavMain() { className="inline-flex cursor-pointer items-center gap-x-2 data-current:bg-sidebar-accent data-current:px-2 data-current:font-medium data-current:text-sidebar-accent-foreground" key={subItem.title} > - + {subItem.title} @@ -176,6 +181,9 @@ export function NavMain() { to={subItem.url} {...(isActive && { 'data-current': '' })} className="inline-flex items-center gap-x-2" + aria-current={isActive ? 'page' : undefined} + prefetch="render" + end > {subItem.title} From 11d7fd1d7ab98c08efec1abea2be7eb014a31bcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20M=20J=20Barata=20Ribeiro?= <122732773+Barata-Ribeiro@users.noreply.github.com> Date: Thu, 23 Jul 2026 14:57:33 -0300 Subject: [PATCH 4/4] feat(routes): add prefetching to link components for improved performance --- app/routes/calculators/index.tsx | 1 + app/routes/converters/index.tsx | 1 + app/routes/programming/index.tsx | 1 + app/routes/utilities/index.tsx | 1 + 4 files changed, 4 insertions(+) diff --git a/app/routes/calculators/index.tsx b/app/routes/calculators/index.tsx index d681091..8b03e53 100644 --- a/app/routes/calculators/index.tsx +++ b/app/routes/calculators/index.tsx @@ -75,6 +75,7 @@ export default function Page() { to={calc.href} aria-describedby={descId} className="block overflow-hidden rounded-md bg-background p-4 transition-shadow duration-150 hover:shadow-md focus:ring-2 focus:ring-ring focus:outline-none" + prefetch="viewport" >

{calc.title}

diff --git a/app/routes/converters/index.tsx b/app/routes/converters/index.tsx index 54c7a9d..ca3a6b1 100644 --- a/app/routes/converters/index.tsx +++ b/app/routes/converters/index.tsx @@ -77,6 +77,7 @@ export default function Index() { to={uc.href} aria-describedby={descId} className="block overflow-hidden rounded-md border bg-background p-4 transition-shadow duration-150 hover:shadow-md focus:ring-2 focus:ring-ring focus:outline-none" + prefetch="viewport" >

{uc.title}

diff --git a/app/routes/programming/index.tsx b/app/routes/programming/index.tsx index 8125620..7dc6e52 100644 --- a/app/routes/programming/index.tsx +++ b/app/routes/programming/index.tsx @@ -57,6 +57,7 @@ export default function Page() { to={p.href} aria-describedby={descId} className="block overflow-hidden rounded-md bg-background p-4 transition-shadow duration-150 hover:shadow-md focus:ring-2 focus:ring-ring focus:outline-none" + prefetch="viewport" >

{p.title}

diff --git a/app/routes/utilities/index.tsx b/app/routes/utilities/index.tsx index 33bfe52..a2d432b 100644 --- a/app/routes/utilities/index.tsx +++ b/app/routes/utilities/index.tsx @@ -78,6 +78,7 @@ export default function Page() { to={u.href} aria-describedby={descId} className="block overflow-hidden rounded-md bg-background p-4 transition-shadow duration-150 hover:shadow-md focus:ring-2 focus:ring-ring focus:outline-none" + prefetch="viewport" >

{u.title}