diff --git a/app/page.tsx b/app/page.tsx index 8649186..c0901cb 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,7 +1,7 @@ "use client"; -import { useState, useEffect } from "react"; -import { aircraftList, Aircraft, SavedAircraft } from "../data/aircraft"; +import { useState, useEffect, useMemo } from "react"; +import { aircraftList, Aircraft, SavedAircraft, EnvelopePoint, Station } from "../data/aircraft"; import ManifestReport from "../components/ManifestReport"; import HangarList from "../components/HangarList"; import SettingsModal from "../components/SettingsModal"; @@ -177,95 +177,99 @@ export default function Home() { }, [customEmptyWeight, customEmptyArm, armOverrides, selectedPlane, savedPlanes, view]); // --- CALCULATION LOGIC --- - let results = { - rampWeight: 0, - rampMoment: 0, // FIX: Initialize rampMoment - takeoffWeight: 0, takeoffMoment: 0, takeoffCG: 0, - landingWeight: 0, landingCG: 0, - isTakeoffSafe: true, isLandingSafe: true, - takeoffIssue: null as string | null, landingIssue: null as string | null, - isGo: true, enduranceHours: 0, enduranceMinutes: 0, - activeEnvelope: [] as any[], maxGross: 0, - fuelArm: 0 - }; + // ⚡ Bolt: Memoize physics calculation to prevent re-running on unrelated UI updates + const results = useMemo(() => { + let res = { + rampWeight: 0, + rampMoment: 0, + takeoffWeight: 0, takeoffMoment: 0, takeoffCG: 0, + landingWeight: 0, landingCG: 0, + isTakeoffSafe: true, isLandingSafe: true, + takeoffIssue: null as string | null, landingIssue: null as string | null, + isGo: true, enduranceHours: 0, enduranceMinutes: 0, + activeEnvelope: [] as EnvelopePoint[], maxGross: 0, + fuelArm: 0 + }; - if (selectedPlane) { - let rampWeight = customEmptyWeight; - let rampMoment = customEmptyWeight * customEmptyArm; - let fuelArm = 0; - let totalFuelWeight = 0; - - selectedPlane.stations.forEach((station: any) => { - let w = weights[station.id] || 0; - const isFuel = station.id.toLowerCase().includes("fuel"); - if (isFuel) { - if (useGallons) w = w * 6; - totalFuelWeight = w; - fuelArm = armOverrides[station.id] !== undefined ? armOverrides[station.id] : station.arm; - } - const arm = armOverrides[station.id] !== undefined ? armOverrides[station.id] : station.arm; - rampWeight += w; - rampMoment += w * arm; - }); - - customStations.forEach((s) => { - rampWeight += s.weight; - rampMoment += s.weight * s.arm; - }); - - const taxiWeight = fuel.taxi * 6; - const takeoffWeight = rampWeight - taxiWeight; - const takeoffMoment = rampMoment - (taxiWeight * fuelArm); - const takeoffCG = takeoffMoment / (takeoffWeight || 1); - - let landingWeight = takeoffWeight; - let landingCG = takeoffCG; - - if (fuel.trip > 0) { - const tripWeight = fuel.trip * 6; - landingWeight = takeoffWeight - tripWeight; - const landingMoment = takeoffMoment - (tripWeight * fuelArm); - landingCG = landingMoment / (landingWeight || 1); - } + if (selectedPlane) { + let rampWeight = customEmptyWeight; + let rampMoment = customEmptyWeight * customEmptyArm; + let fuelArm = 0; + let totalFuelWeight = 0; + + selectedPlane.stations.forEach((station: Station) => { + let w = weights[station.id] || 0; + const isFuel = station.id.toLowerCase().includes("fuel"); + if (isFuel) { + if (useGallons) w = w * 6; + totalFuelWeight = w; + fuelArm = armOverrides[station.id] !== undefined ? armOverrides[station.id] : station.arm; + } + const arm = armOverrides[station.id] !== undefined ? armOverrides[station.id] : station.arm; + rampWeight += w; + rampMoment += w * arm; + }); - const usableTakeoffFuelGal = (totalFuelWeight - taxiWeight) / 6; - let enduranceHours = 0; - let enduranceMinutes = 0; - if (fuel.burn > 0 && usableTakeoffFuelGal > 0) { - const totalHours = usableTakeoffFuelGal / fuel.burn; - enduranceHours = Math.floor(totalHours); - enduranceMinutes = Math.floor((totalHours - enduranceHours) * 60); - } + customStations.forEach((s) => { + rampWeight += s.weight; + rampMoment += s.weight * s.arm; + }); - const activeEnvelope = (category === 'utility' && selectedPlane.utilityEnvelope) ? selectedPlane.utilityEnvelope : selectedPlane.envelope; - const maxGross = Math.max(...activeEnvelope.map((p: any) => p.weight)); - const isTakeoffInside = isPointInPolygon({ cg: takeoffCG, weight: takeoffWeight }, activeEnvelope); - const isLandingInside = isPointInPolygon({ cg: landingCG, weight: landingWeight }, activeEnvelope); - - const takeoffLimits = getCGLimitsAtWeight(takeoffWeight, activeEnvelope); - const landingLimits = getCGLimitsAtWeight(landingWeight, activeEnvelope); - - const getFailureReason = (weight: number, cg: number, limits: {minCG: number, maxCG: number} | null) => { - if (weight > maxGross) return `Over Max Gross (${(weight - maxGross).toFixed(0)} lbs)`; - if (!limits) return "Outside Envelope"; - if (cg < limits.minCG) return `Fwd Limit Exceeded by ${(limits.minCG - cg).toFixed(1)}"`; - if (cg > limits.maxCG) return `Aft Limit Exceeded by ${(cg - limits.maxCG).toFixed(1)}"`; - return "Outside Envelope"; - }; + const taxiWeight = fuel.taxi * 6; + const takeoffWeight = rampWeight - taxiWeight; + const takeoffMoment = rampMoment - (taxiWeight * fuelArm); + const takeoffCG = takeoffMoment / (takeoffWeight || 1); - results = { - rampWeight, - rampMoment, // FIX: Pass calculated moment to results - takeoffWeight, takeoffMoment, takeoffCG, - landingWeight, landingCG, - isTakeoffSafe: isTakeoffInside, isLandingSafe: isLandingInside, - takeoffIssue: !isTakeoffInside ? getFailureReason(takeoffWeight, takeoffCG, takeoffLimits) : null, - landingIssue: !isLandingInside ? getFailureReason(landingWeight, landingCG, landingLimits) : null, - isGo: isTakeoffInside && (toggles.flightPlan ? isLandingInside : true), - enduranceHours, enduranceMinutes, - activeEnvelope, maxGross, fuelArm - }; - } + let landingWeight = takeoffWeight; + let landingCG = takeoffCG; + + if (fuel.trip > 0) { + const tripWeight = fuel.trip * 6; + landingWeight = takeoffWeight - tripWeight; + const landingMoment = takeoffMoment - (tripWeight * fuelArm); + landingCG = landingMoment / (landingWeight || 1); + } + + const usableTakeoffFuelGal = (totalFuelWeight - taxiWeight) / 6; + let enduranceHours = 0; + let enduranceMinutes = 0; + if (fuel.burn > 0 && usableTakeoffFuelGal > 0) { + const totalHours = usableTakeoffFuelGal / fuel.burn; + enduranceHours = Math.floor(totalHours); + enduranceMinutes = Math.floor((totalHours - enduranceHours) * 60); + } + + const activeEnvelope = (category === 'utility' && selectedPlane.utilityEnvelope) ? selectedPlane.utilityEnvelope : selectedPlane.envelope; + const maxGross = Math.max(...activeEnvelope.map((p: EnvelopePoint) => p.weight)); + const isTakeoffInside = isPointInPolygon({ cg: takeoffCG, weight: takeoffWeight }, activeEnvelope); + const isLandingInside = isPointInPolygon({ cg: landingCG, weight: landingWeight }, activeEnvelope); + + const takeoffLimits = getCGLimitsAtWeight(takeoffWeight, activeEnvelope); + const landingLimits = getCGLimitsAtWeight(landingWeight, activeEnvelope); + + const getFailureReason = (weight: number, cg: number, limits: {minCG: number, maxCG: number} | null) => { + if (weight > maxGross) return `Over Max Gross (${(weight - maxGross).toFixed(0)} lbs)`; + if (!limits) return "Outside Envelope"; + if (cg < limits.minCG) return `Fwd Limit Exceeded by ${(limits.minCG - cg).toFixed(1)}"`; + if (cg > limits.maxCG) return `Aft Limit Exceeded by ${(cg - limits.maxCG).toFixed(1)}"`; + return "Outside Envelope"; + }; + + res = { + rampWeight, + rampMoment, + takeoffWeight, takeoffMoment, takeoffCG, + landingWeight, landingCG, + isTakeoffSafe: isTakeoffInside, isLandingSafe: isLandingInside, + takeoffIssue: !isTakeoffInside ? getFailureReason(takeoffWeight, takeoffCG, takeoffLimits) : null, + landingIssue: !isLandingInside ? getFailureReason(landingWeight, landingCG, landingLimits) : null, + isGo: isTakeoffInside && (toggles.flightPlan ? isLandingInside : true), + enduranceHours, enduranceMinutes, + activeEnvelope, maxGross, fuelArm + }; + } + return res; + }, [selectedPlane, customEmptyWeight, customEmptyArm, weights, armOverrides, customStations, fuel, useGallons, category, toggles.flightPlan]); // 1. SHOW LANDING PAGE? if (showLanding) { diff --git a/public/sw.js b/public/sw.js index 88e24e0..964995b 100644 --- a/public/sw.js +++ b/public/sw.js @@ -1 +1 @@ -if(!self.define){let e,s={};const a=(a,n)=>(a=new URL(a+".js",n).href,s[a]||new Promise(s=>{if("document"in self){const e=document.createElement("script");e.src=a,e.onload=s,document.head.appendChild(e)}else e=a,importScripts(a),s()}).then(()=>{let e=s[a];if(!e)throw new Error(`Module ${a} didn’t register its module`);return e}));self.define=(n,i)=>{const t=e||("document"in self?document.currentScript.src:"")||location.href;if(s[t])return;let c={};const r=e=>a(e,t),d={module:{uri:t},exports:c,require:r};s[t]=Promise.all(n.map(e=>d[e]||r(e))).then(e=>(i(...e),c))}}define(["./workbox-f1770938"],function(e){"use strict";importScripts(),self.skipWaiting(),e.clientsClaim(),e.precacheAndRoute([{url:"/_next/static/_Qk99KjPP2iEqDRtSZTeY/_buildManifest.js",revision:"745555c29d619878e260d8023b7014c1"},{url:"/_next/static/_Qk99KjPP2iEqDRtSZTeY/_ssgManifest.js",revision:"b6652df95db52feb4daf4eca35380933"},{url:"/_next/static/chunks/4bd1b696-deba172d32c79f82.js",revision:"deba172d32c79f82"},{url:"/_next/static/chunks/928-34428627dfa9fa40.js",revision:"34428627dfa9fa40"},{url:"/_next/static/chunks/973-e432f2a884511a8e.js",revision:"e432f2a884511a8e"},{url:"/_next/static/chunks/app/_global-error/page-a3d4f8e832675edd.js",revision:"a3d4f8e832675edd"},{url:"/_next/static/chunks/app/_not-found/page-f7e9e5d73062cbe5.js",revision:"f7e9e5d73062cbe5"},{url:"/_next/static/chunks/app/layout-83a3f45ebc4f840c.js",revision:"83a3f45ebc4f840c"},{url:"/_next/static/chunks/app/legal/page-fe212f5a92cb34b9.js",revision:"fe212f5a92cb34b9"},{url:"/_next/static/chunks/app/page-892516dfce2a19f4.js",revision:"892516dfce2a19f4"},{url:"/_next/static/chunks/framework-4e51298db41fcfd4.js",revision:"4e51298db41fcfd4"},{url:"/_next/static/chunks/main-2682d92e5634a569.js",revision:"2682d92e5634a569"},{url:"/_next/static/chunks/main-app-abcb25e568d7cd6e.js",revision:"abcb25e568d7cd6e"},{url:"/_next/static/chunks/next/dist/client/components/builtin/app-error-a3d4f8e832675edd.js",revision:"a3d4f8e832675edd"},{url:"/_next/static/chunks/next/dist/client/components/builtin/forbidden-a3d4f8e832675edd.js",revision:"a3d4f8e832675edd"},{url:"/_next/static/chunks/next/dist/client/components/builtin/global-error-1dc6a36b040319f2.js",revision:"1dc6a36b040319f2"},{url:"/_next/static/chunks/next/dist/client/components/builtin/not-found-a3d4f8e832675edd.js",revision:"a3d4f8e832675edd"},{url:"/_next/static/chunks/next/dist/client/components/builtin/unauthorized-a3d4f8e832675edd.js",revision:"a3d4f8e832675edd"},{url:"/_next/static/chunks/polyfills-42372ed130431b0a.js",revision:"846118c33b2c0e922d7b3a7676f81f6f"},{url:"/_next/static/chunks/webpack-07bf70a7cee35489.js",revision:"07bf70a7cee35489"},{url:"/_next/static/css/7857fc8dde1f0206.css",revision:"7857fc8dde1f0206"},{url:"/_next/static/media/19cfc7226ec3afaa-s.woff2",revision:"9dda5cfc9a46f256d0e131bb535e46f8"},{url:"/_next/static/media/21350d82a1f187e9-s.woff2",revision:"4e2553027f1d60eff32898367dd4d541"},{url:"/_next/static/media/8e9860b6e62d6359-s.woff2",revision:"01ba6c2a184b8cba08b0d57167664d75"},{url:"/_next/static/media/ba9851c3c22cd980-s.woff2",revision:"9e494903d6b0ffec1a1e14d34427d44d"},{url:"/_next/static/media/c5fe6dc8356a8c31-s.woff2",revision:"027a89e9ab733a145db70f09b8a18b42"},{url:"/_next/static/media/df0a9ae256c0569c-s.woff2",revision:"d54db44de5ccb18886ece2fda72bdfe0"},{url:"/_next/static/media/e4af272ccee01ff0-s.p.woff2",revision:"65850a373e258f1c897a2b3d75eb74de"},{url:"/apple-touch-icon.png",revision:"b73ace5d08ca50ae4d207ae49aa324a7"},{url:"/assets/screenshot-calculator.png",revision:"b5fb22478199d5d0a0567d6c3bd2c05d"},{url:"/assets/screenshot-hangar.png",revision:"ebf24315ea400345ca95724c2f38067f"},{url:"/assets/screenshot-report.png",revision:"153a01d7958719252c31e1a52830eb0f"},{url:"/file.svg",revision:"d09f95206c3fa0bb9bd9fefabfd0ea71"},{url:"/globe.svg",revision:"2aaafa6a49b6563925fe440891e32717"},{url:"/icon-192.png",revision:"4cc4ba11db593a4f3899fc2c628e4bfb"},{url:"/icon-512.png",revision:"808fb568cffbb81a989f7e187df3fcc9"},{url:"/manifest.json",revision:"4b7e0c73a2888962193970e2f5bd7149"},{url:"/next.svg",revision:"8e061864f388b47f33a1c3780831193e"},{url:"/swe-worker-5c72df51bb1f6ee0.js",revision:"76fdd3369f623a3edcf74ce2200bfdd0"},{url:"/vercel.svg",revision:"c0af2f507b369b085b35ef4bbe3bcf1e"},{url:"/window.svg",revision:"a2760511c65806022ad20adf74370ff3"}],{ignoreURLParametersMatching:[/^utm_/,/^fbclid$/]}),e.cleanupOutdatedCaches(),e.registerRoute("/",new e.NetworkFirst({cacheName:"start-url",plugins:[{cacheWillUpdate:async({response:e})=>e&&"opaqueredirect"===e.type?new Response(e.body,{status:200,statusText:"OK",headers:e.headers}):e}]}),"GET"),e.registerRoute(/^https:\/\/fonts\.(?:gstatic)\.com\/.*/i,new e.CacheFirst({cacheName:"google-fonts-webfonts",plugins:[new e.ExpirationPlugin({maxEntries:4,maxAgeSeconds:31536e3})]}),"GET"),e.registerRoute(/^https:\/\/fonts\.(?:googleapis)\.com\/.*/i,new e.StaleWhileRevalidate({cacheName:"google-fonts-stylesheets",plugins:[new e.ExpirationPlugin({maxEntries:4,maxAgeSeconds:604800})]}),"GET"),e.registerRoute(/\.(?:eot|otf|ttc|ttf|woff|woff2|font.css)$/i,new e.StaleWhileRevalidate({cacheName:"static-font-assets",plugins:[new e.ExpirationPlugin({maxEntries:4,maxAgeSeconds:604800})]}),"GET"),e.registerRoute(/\.(?:jpg|jpeg|gif|png|svg|ico|webp)$/i,new e.StaleWhileRevalidate({cacheName:"static-image-assets",plugins:[new e.ExpirationPlugin({maxEntries:64,maxAgeSeconds:2592e3})]}),"GET"),e.registerRoute(/\/_next\/static.+\.js$/i,new e.CacheFirst({cacheName:"next-static-js-assets",plugins:[new e.ExpirationPlugin({maxEntries:64,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\/_next\/image\?url=.+$/i,new e.StaleWhileRevalidate({cacheName:"next-image",plugins:[new e.ExpirationPlugin({maxEntries:64,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:mp3|wav|ogg)$/i,new e.CacheFirst({cacheName:"static-audio-assets",plugins:[new e.RangeRequestsPlugin,new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:mp4|webm)$/i,new e.CacheFirst({cacheName:"static-video-assets",plugins:[new e.RangeRequestsPlugin,new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:js)$/i,new e.StaleWhileRevalidate({cacheName:"static-js-assets",plugins:[new e.ExpirationPlugin({maxEntries:48,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:css|less)$/i,new e.StaleWhileRevalidate({cacheName:"static-style-assets",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\/_next\/data\/.+\/.+\.json$/i,new e.StaleWhileRevalidate({cacheName:"next-data",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:json|xml|csv)$/i,new e.NetworkFirst({cacheName:"static-data-assets",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(({sameOrigin:e,url:{pathname:s}})=>!(!e||s.startsWith("/api/auth/callback")||!s.startsWith("/api/")),new e.NetworkFirst({cacheName:"apis",networkTimeoutSeconds:10,plugins:[new e.ExpirationPlugin({maxEntries:16,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(({request:e,url:{pathname:s},sameOrigin:a})=>"1"===e.headers.get("RSC")&&"1"===e.headers.get("Next-Router-Prefetch")&&a&&!s.startsWith("/api/"),new e.NetworkFirst({cacheName:"pages-rsc-prefetch",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(({request:e,url:{pathname:s},sameOrigin:a})=>"1"===e.headers.get("RSC")&&a&&!s.startsWith("/api/"),new e.NetworkFirst({cacheName:"pages-rsc",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(({url:{pathname:e},sameOrigin:s})=>s&&!e.startsWith("/api/"),new e.NetworkFirst({cacheName:"pages",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(({sameOrigin:e})=>!e,new e.NetworkFirst({cacheName:"cross-origin",networkTimeoutSeconds:10,plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:3600})]}),"GET"),self.__WB_DISABLE_DEV_LOGS=!0}); +if(!self.define){let e,s={};const a=(a,n)=>(a=new URL(a+".js",n).href,s[a]||new Promise(s=>{if("document"in self){const e=document.createElement("script");e.src=a,e.onload=s,document.head.appendChild(e)}else e=a,importScripts(a),s()}).then(()=>{let e=s[a];if(!e)throw new Error(`Module ${a} didn’t register its module`);return e}));self.define=(n,i)=>{const t=e||("document"in self?document.currentScript.src:"")||location.href;if(s[t])return;let c={};const d=e=>a(e,t),r={module:{uri:t},exports:c,require:d};s[t]=Promise.all(n.map(e=>r[e]||d(e))).then(e=>(i(...e),c))}}define(["./workbox-f1770938"],function(e){"use strict";importScripts(),self.skipWaiting(),e.clientsClaim(),e.precacheAndRoute([{url:"/_next/static/BVj-2DHsdejq55GxWP5Rx/_buildManifest.js",revision:"4994a1a64b62b600714e5f6227cd3d29"},{url:"/_next/static/BVj-2DHsdejq55GxWP5Rx/_ssgManifest.js",revision:"b6652df95db52feb4daf4eca35380933"},{url:"/_next/static/chunks/4bd1b696-deba172d32c79f82.js",revision:"deba172d32c79f82"},{url:"/_next/static/chunks/500-b84d19d842172eba.js",revision:"b84d19d842172eba"},{url:"/_next/static/chunks/928-34428627dfa9fa40.js",revision:"34428627dfa9fa40"},{url:"/_next/static/chunks/973-54517c363a97a136.js",revision:"54517c363a97a136"},{url:"/_next/static/chunks/app/_global-error/page-a3d4f8e832675edd.js",revision:"a3d4f8e832675edd"},{url:"/_next/static/chunks/app/_not-found/page-f7e9e5d73062cbe5.js",revision:"f7e9e5d73062cbe5"},{url:"/_next/static/chunks/app/layout-83a3f45ebc4f840c.js",revision:"83a3f45ebc4f840c"},{url:"/_next/static/chunks/app/legal/page-d8375d00d5f2fcde.js",revision:"d8375d00d5f2fcde"},{url:"/_next/static/chunks/app/page-633f06a204103b9d.js",revision:"633f06a204103b9d"},{url:"/_next/static/chunks/app/privacy-policy/page-785b7b2aafe031c2.js",revision:"785b7b2aafe031c2"},{url:"/_next/static/chunks/framework-4e51298db41fcfd4.js",revision:"4e51298db41fcfd4"},{url:"/_next/static/chunks/main-2682d92e5634a569.js",revision:"2682d92e5634a569"},{url:"/_next/static/chunks/main-app-abcb25e568d7cd6e.js",revision:"abcb25e568d7cd6e"},{url:"/_next/static/chunks/next/dist/client/components/builtin/app-error-a3d4f8e832675edd.js",revision:"a3d4f8e832675edd"},{url:"/_next/static/chunks/next/dist/client/components/builtin/forbidden-a3d4f8e832675edd.js",revision:"a3d4f8e832675edd"},{url:"/_next/static/chunks/next/dist/client/components/builtin/global-error-1dc6a36b040319f2.js",revision:"1dc6a36b040319f2"},{url:"/_next/static/chunks/next/dist/client/components/builtin/not-found-a3d4f8e832675edd.js",revision:"a3d4f8e832675edd"},{url:"/_next/static/chunks/next/dist/client/components/builtin/unauthorized-a3d4f8e832675edd.js",revision:"a3d4f8e832675edd"},{url:"/_next/static/chunks/polyfills-42372ed130431b0a.js",revision:"846118c33b2c0e922d7b3a7676f81f6f"},{url:"/_next/static/chunks/webpack-07bf70a7cee35489.js",revision:"07bf70a7cee35489"},{url:"/_next/static/css/6fa0bde524d10cf5.css",revision:"6fa0bde524d10cf5"},{url:"/_next/static/media/19cfc7226ec3afaa-s.woff2",revision:"9dda5cfc9a46f256d0e131bb535e46f8"},{url:"/_next/static/media/21350d82a1f187e9-s.woff2",revision:"4e2553027f1d60eff32898367dd4d541"},{url:"/_next/static/media/8e9860b6e62d6359-s.woff2",revision:"01ba6c2a184b8cba08b0d57167664d75"},{url:"/_next/static/media/ba9851c3c22cd980-s.woff2",revision:"9e494903d6b0ffec1a1e14d34427d44d"},{url:"/_next/static/media/c5fe6dc8356a8c31-s.woff2",revision:"027a89e9ab733a145db70f09b8a18b42"},{url:"/_next/static/media/df0a9ae256c0569c-s.woff2",revision:"d54db44de5ccb18886ece2fda72bdfe0"},{url:"/_next/static/media/e4af272ccee01ff0-s.p.woff2",revision:"65850a373e258f1c897a2b3d75eb74de"},{url:"/apple-touch-icon.png",revision:"b73ace5d08ca50ae4d207ae49aa324a7"},{url:"/assets/screenshot-calculator.png",revision:"b5fb22478199d5d0a0567d6c3bd2c05d"},{url:"/assets/screenshot-hangar.png",revision:"ebf24315ea400345ca95724c2f38067f"},{url:"/assets/screenshot-report.png",revision:"153a01d7958719252c31e1a52830eb0f"},{url:"/file.svg",revision:"d09f95206c3fa0bb9bd9fefabfd0ea71"},{url:"/globe.svg",revision:"2aaafa6a49b6563925fe440891e32717"},{url:"/icon-192.png",revision:"4cc4ba11db593a4f3899fc2c628e4bfb"},{url:"/icon-512.png",revision:"808fb568cffbb81a989f7e187df3fcc9"},{url:"/manifest.json",revision:"4b7e0c73a2888962193970e2f5bd7149"},{url:"/next.svg",revision:"8e061864f388b47f33a1c3780831193e"},{url:"/swe-worker-5c72df51bb1f6ee0.js",revision:"76fdd3369f623a3edcf74ce2200bfdd0"},{url:"/vercel.svg",revision:"c0af2f507b369b085b35ef4bbe3bcf1e"},{url:"/window.svg",revision:"a2760511c65806022ad20adf74370ff3"}],{ignoreURLParametersMatching:[/^utm_/,/^fbclid$/]}),e.cleanupOutdatedCaches(),e.registerRoute("/",new e.NetworkFirst({cacheName:"start-url",plugins:[{cacheWillUpdate:async({response:e})=>e&&"opaqueredirect"===e.type?new Response(e.body,{status:200,statusText:"OK",headers:e.headers}):e}]}),"GET"),e.registerRoute(/^https:\/\/fonts\.(?:gstatic)\.com\/.*/i,new e.CacheFirst({cacheName:"google-fonts-webfonts",plugins:[new e.ExpirationPlugin({maxEntries:4,maxAgeSeconds:31536e3})]}),"GET"),e.registerRoute(/^https:\/\/fonts\.(?:googleapis)\.com\/.*/i,new e.StaleWhileRevalidate({cacheName:"google-fonts-stylesheets",plugins:[new e.ExpirationPlugin({maxEntries:4,maxAgeSeconds:604800})]}),"GET"),e.registerRoute(/\.(?:eot|otf|ttc|ttf|woff|woff2|font.css)$/i,new e.StaleWhileRevalidate({cacheName:"static-font-assets",plugins:[new e.ExpirationPlugin({maxEntries:4,maxAgeSeconds:604800})]}),"GET"),e.registerRoute(/\.(?:jpg|jpeg|gif|png|svg|ico|webp)$/i,new e.StaleWhileRevalidate({cacheName:"static-image-assets",plugins:[new e.ExpirationPlugin({maxEntries:64,maxAgeSeconds:2592e3})]}),"GET"),e.registerRoute(/\/_next\/static.+\.js$/i,new e.CacheFirst({cacheName:"next-static-js-assets",plugins:[new e.ExpirationPlugin({maxEntries:64,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\/_next\/image\?url=.+$/i,new e.StaleWhileRevalidate({cacheName:"next-image",plugins:[new e.ExpirationPlugin({maxEntries:64,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:mp3|wav|ogg)$/i,new e.CacheFirst({cacheName:"static-audio-assets",plugins:[new e.RangeRequestsPlugin,new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:mp4|webm)$/i,new e.CacheFirst({cacheName:"static-video-assets",plugins:[new e.RangeRequestsPlugin,new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:js)$/i,new e.StaleWhileRevalidate({cacheName:"static-js-assets",plugins:[new e.ExpirationPlugin({maxEntries:48,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:css|less)$/i,new e.StaleWhileRevalidate({cacheName:"static-style-assets",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\/_next\/data\/.+\/.+\.json$/i,new e.StaleWhileRevalidate({cacheName:"next-data",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:json|xml|csv)$/i,new e.NetworkFirst({cacheName:"static-data-assets",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(({sameOrigin:e,url:{pathname:s}})=>!(!e||s.startsWith("/api/auth/callback")||!s.startsWith("/api/")),new e.NetworkFirst({cacheName:"apis",networkTimeoutSeconds:10,plugins:[new e.ExpirationPlugin({maxEntries:16,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(({request:e,url:{pathname:s},sameOrigin:a})=>"1"===e.headers.get("RSC")&&"1"===e.headers.get("Next-Router-Prefetch")&&a&&!s.startsWith("/api/"),new e.NetworkFirst({cacheName:"pages-rsc-prefetch",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(({request:e,url:{pathname:s},sameOrigin:a})=>"1"===e.headers.get("RSC")&&a&&!s.startsWith("/api/"),new e.NetworkFirst({cacheName:"pages-rsc",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(({url:{pathname:e},sameOrigin:s})=>s&&!e.startsWith("/api/"),new e.NetworkFirst({cacheName:"pages",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(({sameOrigin:e})=>!e,new e.NetworkFirst({cacheName:"cross-origin",networkTimeoutSeconds:10,plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:3600})]}),"GET"),self.__WB_DISABLE_DEV_LOGS=!0});