Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(next/image): washed out blur placeholder #52583

Merged
merged 22 commits into from Jul 26, 2023

Conversation

arturbien
Copy link
Contributor

@arturbien arturbien commented Jul 12, 2023

Fixes #52548

See this tweet thread for more info: https://twitter.com/artur_bien/status/1688626080016437249

This PR fixes the issue of the <Image /> "blur" placeholder where the placeholder image appears "washed out" and feathered around the edges.

The fix does NOT involve any API changes - only the underlying SVG filter is updated.
The filter works for any image type (JPEG, GIF, WEBP) no matter if it contains transparency or not.

How it works

  <filter id='b' color-interpolation-filters='sRGB'>
    <feMorphology in='SourceAlpha' operator='dilate' radius='15' result='dilate' />
    <feGaussianBlur in='dilate' stdDeviation='15' result='mask' />
    <feGaussianBlur in='SourceGraphic' stdDeviation='20' result='blur' />
    <feComponentTransfer in='blur' result='solid'>
      <feFuncA type='table' tableValues='1 1' />
    </feComponentTransfer>
    <feComposite in2='mask' in='solid' operator='in' result="comp" />
    <feMerge>
      <feMergeNode in="SourceGraphic" />
      <feMergeNode in="comp" />
    </feMerge>
  </filter>

Currently the underlying filter uses feComponentTransfer to get rid of the feathered edges but only for JPEG images. This is because if alpha channel was to be mapped to 1 for images with transparency in it, all of the transparent pixels would turn black (which is obviously undesirable).

The way I fixed it is by creating a mask from the SourceAlpha (effectively the "shape" of the object in an image), slightly blurring it for a smoother look (feMorphology + feGaussianBlur), and then clipping the feComponentTransfer result to the shape of the mask (<feComposite in2='mask' in='solid' operator='in' result="comp" />).

Then finally feMerge is used to stack original image and the clipping result from the previous step just to make sure that any left over artifacts from clipping are hidden.

Result

Here's a comparison between the current implementation (left column), and the filter above (right column)

Screenshot 2023-07-12 at 10 44 56

@ijjk
Copy link
Member

ijjk commented Jul 12, 2023

Allow CI Workflow Run

  • approve CI run for commit: 6822c0d

Note: this should only be enabled once the PR is ready to go and can only be enabled by a maintainer

@arturbien arturbien marked this pull request as draft July 12, 2023 09:41
@ijjk
Copy link
Member

ijjk commented Jul 12, 2023

Tests Passed

@styfle
Copy link
Member

styfle commented Jul 12, 2023

Thanks! Please update the failing tests

@arturbien arturbien marked this pull request as ready for review July 12, 2023 16:34
Copy link
Member

@styfle styfle left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A couple suggestions

packages/next/src/shared/lib/image-blur-svg.ts Outdated Show resolved Hide resolved
@arturbien
Copy link
Contributor Author

@styfle hard for me to decypher the failing CI message. Seems like some workflows have to be first enabled by some maintainer? When I've updated the tests and ran them locally they completed 🤔

@arturbien arturbien requested a review from styfle July 12, 2023 17:35
@ijjk
Copy link
Member

ijjk commented Jul 12, 2023

Stats from current PR

Default Build (Decrease detected ✓)
General Overall decrease ✓
vercel/next.js canary arturbien/next.js fix/image-blur-placeholder Change
buildDuration 40.8s 42.7s ⚠️ +1.9s
buildDurationCached 17.9s 16.7s -1.2s
nodeModulesSize 137 MB 137 MB -776 B
nextStartRea..uration (ms) 547ms 501ms -46ms
Client Bundles (main, webpack) Overall decrease ✓
vercel/next.js canary arturbien/next.js fix/image-blur-placeholder Change
35a5f01-HASH.js gzip 50.5 kB 50.5 kB
446.HASH.js gzip 185 B 181 B -4 B
98-HASH.js gzip 25.7 kB 25.7 kB -16 B
framework-HASH.js gzip 45.2 kB 45.2 kB
main-app-HASH.js gzip 217 B 220 B ⚠️ +3 B
main-HASH.js gzip 28.6 kB 28.6 kB ⚠️ +9 B
webpack-HASH.js gzip 1.7 kB 1.7 kB ⚠️ +2 B
Overall change 152 kB 152 kB -6 B
Legacy Client Bundles (polyfills)
vercel/next.js canary arturbien/next.js fix/image-blur-placeholder Change
polyfills-HASH.js gzip 31 kB 31 kB
Overall change 31 kB 31 kB
Client Pages Overall decrease ✓
vercel/next.js canary arturbien/next.js fix/image-blur-placeholder Change
_app-HASH.js gzip 195 B 195 B
_error-HASH.js gzip 182 B 182 B
amp-HASH.js gzip 504 B 503 B -1 B
css-HASH.js gzip 321 B 322 B ⚠️ +1 B
dynamic-HASH.js gzip 2.5 kB 2.5 kB -3 B
edge-ssr-HASH.js gzip 256 B 255 B -1 B
head-HASH.js gzip 350 B 349 B -1 B
hooks-HASH.js gzip 368 B 369 B ⚠️ +1 B
image-HASH.js gzip 4.27 kB 4.27 kB ⚠️ +3 B
index-HASH.js gzip 255 B 256 B ⚠️ +1 B
link-HASH.js gzip 2.62 kB 2.62 kB -4 B
routerDirect..HASH.js gzip 312 B 313 B ⚠️ +1 B
script-HASH.js gzip 384 B 385 B ⚠️ +1 B
withRouter-HASH.js gzip 308 B 309 B ⚠️ +1 B
1afbb74e6ecf..834.css gzip 106 B 106 B
Overall change 12.9 kB 12.9 kB -1 B
Client Build Manifests Overall decrease ✓
vercel/next.js canary arturbien/next.js fix/image-blur-placeholder Change
_buildManifest.js gzip 485 B 480 B -5 B
Overall change 485 B 480 B -5 B
Rendered Page Sizes Overall decrease ✓
vercel/next.js canary arturbien/next.js fix/image-blur-placeholder Change
index.html gzip 511 B 511 B
link.html gzip 526 B 525 B -1 B
withRouter.html gzip 505 B 505 B
Overall change 1.54 kB 1.54 kB -1 B
Edge SSR bundle Size Overall decrease ✓
vercel/next.js canary arturbien/next.js fix/image-blur-placeholder Change
edge-ssr.js gzip 87.9 kB 87.9 kB -3 B
page.js gzip 145 kB 145 kB -9 B
Overall change 233 kB 233 kB -12 B
Middleware size Overall decrease ✓
vercel/next.js canary arturbien/next.js fix/image-blur-placeholder Change
middleware-b..fest.js gzip 626 B 620 B -6 B
middleware-r..fest.js gzip 151 B 151 B
middleware.js gzip 16.8 kB 16.8 kB ⚠️ +3 B
edge-runtime..pack.js gzip 1.83 kB 1.83 kB
Overall change 19.4 kB 19.4 kB -3 B

Diffs

Diff for page.js

Diff too large to display

Diff for middleware-b..-manifest.js
@@ -7,96 +7,96 @@ self.__BUILD_MANIFEST = {
     "static/BUILD_ID/_ssgManifest.js",
   ],
   rootMainFiles: [
-    "static/chunks/webpack-b6c94c729e6804bb.js",
-    "static/chunks/35a5f01f-5c9989672264be1c.js",
-    "static/chunks/98-ff976472d3228ce2.js",
-    "static/chunks/main-app-a382b2755d2cc5f0.js",
+    "static/chunks/webpack-917c5126fa399878.js",
+    "static/chunks/9ace19b1-d5b4f46ecfecaeb7.js",
+    "static/chunks/819-b9432e619450e671.js",
+    "static/chunks/main-app-f138fd48dec2336e.js",
   ],
   pages: {
     "/": [
-      "static/chunks/webpack-b6c94c729e6804bb.js",
+      "static/chunks/webpack-917c5126fa399878.js",
       "static/chunks/framework-114cbddc60b679b8.js",
-      "static/chunks/main-eec29850636cb2ca.js",
-      "static/chunks/pages/index-b3eeca82dc15d3ca.js",
+      "static/chunks/main-1bbe9bbce917d393.js",
+      "static/chunks/pages/index-94016d70d7482fe7.js",
     ],
     "/_app": [
-      "static/chunks/webpack-b6c94c729e6804bb.js",
+      "static/chunks/webpack-917c5126fa399878.js",
       "static/chunks/framework-114cbddc60b679b8.js",
-      "static/chunks/main-eec29850636cb2ca.js",
-      "static/chunks/pages/_app-7ba896576f11cc13.js",
+      "static/chunks/main-1bbe9bbce917d393.js",
+      "static/chunks/pages/_app-46cf6cb90b19c472.js",
     ],
     "/_error": [
-      "static/chunks/webpack-b6c94c729e6804bb.js",
+      "static/chunks/webpack-917c5126fa399878.js",
       "static/chunks/framework-114cbddc60b679b8.js",
-      "static/chunks/main-eec29850636cb2ca.js",
-      "static/chunks/pages/_error-95fd436e01e410ce.js",
+      "static/chunks/main-1bbe9bbce917d393.js",
+      "static/chunks/pages/_error-a56f0e852cd19c98.js",
     ],
     "/amp": [
-      "static/chunks/webpack-b6c94c729e6804bb.js",
+      "static/chunks/webpack-917c5126fa399878.js",
       "static/chunks/framework-114cbddc60b679b8.js",
-      "static/chunks/main-eec29850636cb2ca.js",
-      "static/chunks/pages/amp-f480a7f42402bc6b.js",
+      "static/chunks/main-1bbe9bbce917d393.js",
+      "static/chunks/pages/amp-8c903ea74383c3c2.js",
     ],
     "/css": [
-      "static/chunks/webpack-b6c94c729e6804bb.js",
+      "static/chunks/webpack-917c5126fa399878.js",
       "static/chunks/framework-114cbddc60b679b8.js",
-      "static/chunks/main-eec29850636cb2ca.js",
+      "static/chunks/main-1bbe9bbce917d393.js",
       "static/css/ded6b86ab9cc0a1f.css",
-      "static/chunks/pages/css-6cba698e124cc650.js",
+      "static/chunks/pages/css-0d504051a85f0fe7.js",
     ],
     "/dynamic": [
-      "static/chunks/webpack-b6c94c729e6804bb.js",
+      "static/chunks/webpack-917c5126fa399878.js",
       "static/chunks/framework-114cbddc60b679b8.js",
-      "static/chunks/main-eec29850636cb2ca.js",
-      "static/chunks/pages/dynamic-1cd0163617ec6914.js",
+      "static/chunks/main-1bbe9bbce917d393.js",
+      "static/chunks/pages/dynamic-c49fc16237dca2a5.js",
     ],
     "/edge-ssr": [
-      "static/chunks/webpack-b6c94c729e6804bb.js",
+      "static/chunks/webpack-917c5126fa399878.js",
       "static/chunks/framework-114cbddc60b679b8.js",
-      "static/chunks/main-eec29850636cb2ca.js",
-      "static/chunks/pages/edge-ssr-845a7224bdb4411b.js",
+      "static/chunks/main-1bbe9bbce917d393.js",
+      "static/chunks/pages/edge-ssr-5745a64fa421cb05.js",
     ],
     "/head": [
-      "static/chunks/webpack-b6c94c729e6804bb.js",
+      "static/chunks/webpack-917c5126fa399878.js",
       "static/chunks/framework-114cbddc60b679b8.js",
-      "static/chunks/main-eec29850636cb2ca.js",
-      "static/chunks/pages/head-542d60b8442e93ee.js",
+      "static/chunks/main-1bbe9bbce917d393.js",
+      "static/chunks/pages/head-d93013a29556304c.js",
     ],
     "/hooks": [
-      "static/chunks/webpack-b6c94c729e6804bb.js",
+      "static/chunks/webpack-917c5126fa399878.js",
       "static/chunks/framework-114cbddc60b679b8.js",
-      "static/chunks/main-eec29850636cb2ca.js",
-      "static/chunks/pages/hooks-e82577e955d3ddd0.js",
+      "static/chunks/main-1bbe9bbce917d393.js",
+      "static/chunks/pages/hooks-78c139c1b3a7077d.js",
     ],
     "/image": [
-      "static/chunks/webpack-b6c94c729e6804bb.js",
+      "static/chunks/webpack-917c5126fa399878.js",
       "static/chunks/framework-114cbddc60b679b8.js",
-      "static/chunks/main-eec29850636cb2ca.js",
-      "static/chunks/pages/image-6bb5b6e47660a843.js",
+      "static/chunks/main-1bbe9bbce917d393.js",
+      "static/chunks/pages/image-11c16bf6265369a7.js",
     ],
     "/link": [
-      "static/chunks/webpack-b6c94c729e6804bb.js",
+      "static/chunks/webpack-917c5126fa399878.js",
       "static/chunks/framework-114cbddc60b679b8.js",
-      "static/chunks/main-eec29850636cb2ca.js",
-      "static/chunks/pages/link-c0ec4cd09f99730f.js",
+      "static/chunks/main-1bbe9bbce917d393.js",
+      "static/chunks/pages/link-07c1abec25dfa204.js",
     ],
     "/routerDirect": [
-      "static/chunks/webpack-b6c94c729e6804bb.js",
+      "static/chunks/webpack-917c5126fa399878.js",
       "static/chunks/framework-114cbddc60b679b8.js",
-      "static/chunks/main-eec29850636cb2ca.js",
-      "static/chunks/pages/routerDirect-a4378fdd445a6dba.js",
+      "static/chunks/main-1bbe9bbce917d393.js",
+      "static/chunks/pages/routerDirect-21980ec88959c07d.js",
     ],
     "/script": [
-      "static/chunks/webpack-b6c94c729e6804bb.js",
+      "static/chunks/webpack-917c5126fa399878.js",
       "static/chunks/framework-114cbddc60b679b8.js",
-      "static/chunks/main-eec29850636cb2ca.js",
-      "static/chunks/pages/script-0c22bdc31d7a54f9.js",
+      "static/chunks/main-1bbe9bbce917d393.js",
+      "static/chunks/pages/script-4ac368a5fe7f6515.js",
     ],
     "/withRouter": [
-      "static/chunks/webpack-b6c94c729e6804bb.js",
+      "static/chunks/webpack-917c5126fa399878.js",
       "static/chunks/framework-114cbddc60b679b8.js",
-      "static/chunks/main-eec29850636cb2ca.js",
-      "static/chunks/pages/withRouter-1c13d6b4c43864b1.js",
+      "static/chunks/main-1bbe9bbce917d393.js",
+      "static/chunks/pages/withRouter-cd6c8ed384d35879.js",
     ],
   },
   ampFirstPages: [],
Diff for middleware-r..-manifest.js
@@ -1,2 +1,2 @@
 self.__REACT_LOADABLE_MANIFEST =
-  '{"dynamic.js -> ../components/hello":{"id":3446,"files":["static/chunks/446.36d8405b831b4d8d.js"]}}';
+  '{"dynamic.js -> ../components/hello":{"id":8574,"files":["static/chunks/574.6ec7ce57fcf094de.js"]}}';
Diff for middleware.js
@@ -2,7 +2,7 @@
 (self["webpackChunk_N_E"] = self["webpackChunk_N_E"] || []).push([
   [826],
   {
-    /***/ 2551: /***/ (
+    /***/ 3353: /***/ (
       __unused_webpack_module,
       __webpack_exports__,
       __webpack_require__
@@ -22,7 +22,7 @@
       __webpack_require__.r(middleware_namespaceObject);
       __webpack_require__.d(middleware_namespaceObject, {
         default: () => middleware,
-      }); // CONCATENATED MODULE: ./node_modules/.pnpm/file+..+main-repo+packages+next+next-packed.tgz_biqbaboplfbrettd7655fr4n2y/node_modules/next/dist/esm/server/web/globals.js
+      }); // CONCATENATED MODULE: ./node_modules/.pnpm/file+..+diff-repo+packages+next+next-packed.tgz_biqbaboplfbrettd7655fr4n2y/node_modules/next/dist/esm/server/web/globals.js
 
       async function registerInstrumentation() {
         if (
@@ -92,7 +92,7 @@ Learn More: https://nextjs.org/docs/messages/node-module-in-edge-runtime`;
         // Eagerly fire instrumentation hook to make the startup faster.
         void ensureInstrumentationRegistered();
       }
-      enhanceGlobals(); //# sourceMappingURL=globals.js.map // CONCATENATED MODULE: ./node_modules/.pnpm/file+..+main-repo+packages+next+next-packed.tgz_biqbaboplfbrettd7655fr4n2y/node_modules/next/dist/esm/server/web/error.js
+      enhanceGlobals(); //# sourceMappingURL=globals.js.map // CONCATENATED MODULE: ./node_modules/.pnpm/file+..+diff-repo+packages+next+next-packed.tgz_biqbaboplfbrettd7655fr4n2y/node_modules/next/dist/esm/server/web/error.js
 
       class PageSignatureError extends Error {
         constructor({ page }) {
@@ -119,7 +119,7 @@ Learn More: https://nextjs.org/docs/messages/node-module-in-edge-runtime`;
   Read more: https://nextjs.org/docs/messages/middleware-parse-user-agent
   `);
         }
-      } //# sourceMappingURL=error.js.map // CONCATENATED MODULE: ./node_modules/.pnpm/file+..+main-repo+packages+next+next-packed.tgz_biqbaboplfbrettd7655fr4n2y/node_modules/next/dist/esm/server/web/utils.js
+      } //# sourceMappingURL=error.js.map // CONCATENATED MODULE: ./node_modules/.pnpm/file+..+diff-repo+packages+next+next-packed.tgz_biqbaboplfbrettd7655fr4n2y/node_modules/next/dist/esm/server/web/utils.js
 
       /**
        * Converts a Node.js IncomingHttpHeaders object to a Headers object. Any
@@ -254,7 +254,7 @@ Learn More: https://nextjs.org/docs/messages/node-module-in-edge-runtime`;
             }
           );
         }
-      } //# sourceMappingURL=utils.js.map // CONCATENATED MODULE: ./node_modules/.pnpm/file+..+main-repo+packages+next+next-packed.tgz_biqbaboplfbrettd7655fr4n2y/node_modules/next/dist/esm/server/web/spec-extension/fetch-event.js
+      } //# sourceMappingURL=utils.js.map // CONCATENATED MODULE: ./node_modules/.pnpm/file+..+diff-repo+packages+next+next-packed.tgz_biqbaboplfbrettd7655fr4n2y/node_modules/next/dist/esm/server/web/spec-extension/fetch-event.js
 
       const responseSymbol = Symbol("response");
       const passThroughSymbol = Symbol("passThrough");
@@ -300,7 +300,7 @@ Learn More: https://nextjs.org/docs/messages/node-module-in-edge-runtime`;
             page: this.sourcePage,
           });
         }
-      } //# sourceMappingURL=fetch-event.js.map // CONCATENATED MODULE: ./node_modules/.pnpm/file+..+main-repo+packages+next+next-packed.tgz_biqbaboplfbrettd7655fr4n2y/node_modules/next/dist/esm/shared/lib/i18n/detect-domain-locale.js
+      } //# sourceMappingURL=fetch-event.js.map // CONCATENATED MODULE: ./node_modules/.pnpm/file+..+diff-repo+packages+next+next-packed.tgz_biqbaboplfbrettd7655fr4n2y/node_modules/next/dist/esm/shared/lib/i18n/detect-domain-locale.js
 
       function detectDomainLocale(domainItems, hostname, detectedLocale) {
         if (!domainItems) return;
@@ -326,7 +326,7 @@ Learn More: https://nextjs.org/docs/messages/node-module-in-edge-runtime`;
             return item;
           }
         }
-      } //# sourceMappingURL=detect-domain-locale.js.map // CONCATENATED MODULE: ./node_modules/.pnpm/file+..+main-repo+packages+next+next-packed.tgz_biqbaboplfbrettd7655fr4n2y/node_modules/next/dist/esm/shared/lib/router/utils/remove-trailing-slash.js
+      } //# sourceMappingURL=detect-domain-locale.js.map // CONCATENATED MODULE: ./node_modules/.pnpm/file+..+diff-repo+packages+next+next-packed.tgz_biqbaboplfbrettd7655fr4n2y/node_modules/next/dist/esm/shared/lib/router/utils/remove-trailing-slash.js
 
       /**
        * Removes the trailing slash for a given route or page path. Preserves the
@@ -336,7 +336,7 @@ Learn More: https://nextjs.org/docs/messages/node-module-in-edge-runtime`;
        *   - `/` -> `/`
        */ function removeTrailingSlash(route) {
         return route.replace(/\/$/, "") || "/";
-      } //# sourceMappingURL=remove-trailing-slash.js.map // CONCATENATED MODULE: ./node_modules/.pnpm/file+..+main-repo+packages+next+next-packed.tgz_biqbaboplfbrettd7655fr4n2y/node_modules/next/dist/esm/shared/lib/router/utils/parse-path.js
+      } //# sourceMappingURL=remove-trailing-slash.js.map // CONCATENATED MODULE: ./node_modules/.pnpm/file+..+diff-repo+packages+next+next-packed.tgz_biqbaboplfbrettd7655fr4n2y/node_modules/next/dist/esm/shared/lib/router/utils/parse-path.js
 
       /**
        * Given a path this function will find the pathname, query and hash and return
@@ -364,7 +364,7 @@ Learn More: https://nextjs.org/docs/messages/node-module-in-edge-runtime`;
           query: "",
           hash: "",
         };
-      } //# sourceMappingURL=parse-path.js.map // CONCATENATED MODULE: ./node_modules/.pnpm/file+..+main-repo+packages+next+next-packed.tgz_biqbaboplfbrettd7655fr4n2y/node_modules/next/dist/esm/shared/lib/router/utils/add-path-prefix.js
+      } //# sourceMappingURL=parse-path.js.map // CONCATENATED MODULE: ./node_modules/.pnpm/file+..+diff-repo+packages+next+next-packed.tgz_biqbaboplfbrettd7655fr4n2y/node_modules/next/dist/esm/shared/lib/router/utils/add-path-prefix.js
 
       /**
        * Adds the provided prefix to the given path. It first ensures that the path
@@ -375,7 +375,7 @@ Learn More: https://nextjs.org/docs/messages/node-module-in-edge-runtime`;
         }
         const { pathname, query, hash } = parsePath(path);
         return "" + prefix + pathname + query + hash;
-      } //# sourceMappingURL=add-path-prefix.js.map // CONCATENATED MODULE: ./node_modules/.pnpm/file+..+main-repo+packages+next+next-packed.tgz_biqbaboplfbrettd7655fr4n2y/node_modules/next/dist/esm/shared/lib/router/utils/add-path-suffix.js
+      } //# sourceMappingURL=add-path-prefix.js.map // CONCATENATED MODULE: ./node_modules/.pnpm/file+..+diff-repo+packages+next+next-packed.tgz_biqbaboplfbrettd7655fr4n2y/node_modules/next/dist/esm/shared/lib/router/utils/add-path-suffix.js
 
       /**
        * Similarly to `addPathPrefix`, this function adds a suffix at the end on the
@@ -387,7 +387,7 @@ Learn More: https://nextjs.org/docs/messages/node-module-in-edge-runtime`;
         }
         const { pathname, query, hash } = parsePath(path);
         return "" + pathname + suffix + query + hash;
-      } //# sourceMappingURL=add-path-suffix.js.map // CONCATENATED MODULE: ./node_modules/.pnpm/file+..+main-repo+packages+next+next-packed.tgz_biqbaboplfbrettd7655fr4n2y/node_modules/next/dist/esm/shared/lib/router/utils/path-has-prefix.js
+      } //# sourceMappingURL=add-path-suffix.js.map // CONCATENATED MODULE: ./node_modules/.pnpm/file+..+diff-repo+packages+next+next-packed.tgz_biqbaboplfbrettd7655fr4n2y/node_modules/next/dist/esm/shared/lib/router/utils/path-has-prefix.js
 
       /**
        * Checks if a given path starts with a given prefix. It ensures it matches
@@ -401,7 +401,7 @@ Learn More: https://nextjs.org/docs/messages/node-module-in-edge-runtime`;
         }
         const { pathname } = parsePath(path);
         return pathname === prefix || pathname.startsWith(prefix + "/");
-      } //# sourceMappingURL=path-has-prefix.js.map // CONCATENATED MODULE: ./node_modules/.pnpm/file+..+main-repo+packages+next+next-packed.tgz_biqbaboplfbrettd7655fr4n2y/node_modules/next/dist/esm/shared/lib/router/utils/add-locale.js
+      } //# sourceMappingURL=path-has-prefix.js.map // CONCATENATED MODULE: ./node_modules/.pnpm/file+..+diff-repo+packages+next+next-packed.tgz_biqbaboplfbrettd7655fr4n2y/node_modules/next/dist/esm/shared/lib/router/utils/add-locale.js
 
       /**
        * For a given path and a locale, if the locale is given, it will prefix the
@@ -420,7 +420,7 @@ Learn More: https://nextjs.org/docs/messages/node-module-in-edge-runtime`;
         }
         // Add the locale prefix to the path.
         return addPathPrefix(path, "/" + locale);
-      } //# sourceMappingURL=add-locale.js.map // CONCATENATED MODULE: ./node_modules/.pnpm/file+..+main-repo+packages+next+next-packed.tgz_biqbaboplfbrettd7655fr4n2y/node_modules/next/dist/esm/shared/lib/router/utils/format-next-pathname-info.js
+      } //# sourceMappingURL=add-locale.js.map // CONCATENATED MODULE: ./node_modules/.pnpm/file+..+diff-repo+packages+next+next-packed.tgz_biqbaboplfbrettd7655fr4n2y/node_modules/next/dist/esm/shared/lib/router/utils/format-next-pathname-info.js
 
       function formatNextPathnameInfo(info) {
         let pathname = addLocale(
@@ -444,7 +444,7 @@ Learn More: https://nextjs.org/docs/messages/node-module-in-edge-runtime`;
             ? addPathSuffix(pathname, "/")
             : pathname
           : removeTrailingSlash(pathname);
-      } //# sourceMappingURL=format-next-pathname-info.js.map // CONCATENATED MODULE: ./node_modules/.pnpm/file+..+main-repo+packages+next+next-packed.tgz_biqbaboplfbrettd7655fr4n2y/node_modules/next/dist/esm/shared/lib/get-hostname.js
+      } //# sourceMappingURL=format-next-pathname-info.js.map // CONCATENATED MODULE: ./node_modules/.pnpm/file+..+diff-repo+packages+next+next-packed.tgz_biqbaboplfbrettd7655fr4n2y/node_modules/next/dist/esm/shared/lib/get-hostname.js
 
       /**
        * Takes an object with a hostname property (like a parsed URL) and some
@@ -464,7 +464,7 @@ Learn More: https://nextjs.org/docs/messages/node-module-in-edge-runtime`;
           hostname = parsed.hostname;
         } else return;
         return hostname.toLowerCase();
-      } //# sourceMappingURL=get-hostname.js.map // CONCATENATED MODULE: ./node_modules/.pnpm/file+..+main-repo+packages+next+next-packed.tgz_biqbaboplfbrettd7655fr4n2y/node_modules/next/dist/esm/shared/lib/i18n/normalize-locale-path.js
+      } //# sourceMappingURL=get-hostname.js.map // CONCATENATED MODULE: ./node_modules/.pnpm/file+..+diff-repo+packages+next+next-packed.tgz_biqbaboplfbrettd7655fr4n2y/node_modules/next/dist/esm/shared/lib/i18n/normalize-locale-path.js
 
       /**
        * For a pathname that may include a locale from a list of locales, it
@@ -494,7 +494,7 @@ Learn More: https://nextjs.org/docs/messages/node-module-in-edge-runtime`;
           pathname,
           detectedLocale,
         };
-      } //# sourceMappingURL=normalize-locale-path.js.map // CONCATENATED MODULE: ./node_modules/.pnpm/file+..+main-repo+packages+next+next-packed.tgz_biqbaboplfbrettd7655fr4n2y/node_modules/next/dist/esm/shared/lib/router/utils/remove-path-prefix.js
+      } //# sourceMappingURL=normalize-locale-path.js.map // CONCATENATED MODULE: ./node_modules/.pnpm/file+..+diff-repo+packages+next+next-packed.tgz_biqbaboplfbrettd7655fr4n2y/node_modules/next/dist/esm/shared/lib/router/utils/remove-path-prefix.js
 
       /**
        * Given a path and a prefix it will remove the prefix when it exists in the
@@ -528,7 +528,7 @@ Learn More: https://nextjs.org/docs/messages/node-module-in-edge-runtime`;
         // If the path without the prefix doesn't start with a `/` we need to add it
         // back to the path to make sure it's a valid path.
         return "/" + withoutPrefix;
-      } //# sourceMappingURL=remove-path-prefix.js.map // CONCATENATED MODULE: ./node_modules/.pnpm/file+..+main-repo+packages+next+next-packed.tgz_biqbaboplfbrettd7655fr4n2y/node_modules/next/dist/esm/shared/lib/router/utils/get-next-pathname-info.js
+      } //# sourceMappingURL=remove-path-prefix.js.map // CONCATENATED MODULE: ./node_modules/.pnpm/file+..+diff-repo+packages+next+next-packed.tgz_biqbaboplfbrettd7655fr4n2y/node_modules/next/dist/esm/shared/lib/router/utils/get-next-pathname-info.js
 
       function getNextPathnameInfo(pathname, options) {
         var _options_nextConfig;
@@ -579,7 +579,7 @@ Learn More: https://nextjs.org/docs/messages/node-module-in-edge-runtime`;
               : info.pathname;
         }
         return info;
-      } //# sourceMappingURL=get-next-pathname-info.js.map // CONCATENATED MODULE: ./node_modules/.pnpm/file+..+main-repo+packages+next+next-packed.tgz_biqbaboplfbrettd7655fr4n2y/node_modules/next/dist/esm/server/web/next-url.js
+      } //# sourceMappingURL=get-next-pathname-info.js.map // CONCATENATED MODULE: ./node_modules/.pnpm/file+..+diff-repo+packages+next+next-packed.tgz_biqbaboplfbrettd7655fr4n2y/node_modules/next/dist/esm/server/web/next-url.js
 
       const REGEX_LOCALHOST_HOSTNAME =
         /(?!^https?:\/\/)(127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}|::1|localhost)/;
@@ -806,8 +806,8 @@ Learn More: https://nextjs.org/docs/messages/node-module-in-edge-runtime`;
         }
       } //# sourceMappingURL=next-url.js.map
 
-      // EXTERNAL MODULE: ./node_modules/.pnpm/file+..+main-repo+packages+next+next-packed.tgz_biqbaboplfbrettd7655fr4n2y/node_modules/next/dist/compiled/@edge-runtime/cookies/index.js
-      var cookies = __webpack_require__(7238); // CONCATENATED MODULE: ./node_modules/.pnpm/file+..+main-repo+packages+next+next-packed.tgz_biqbaboplfbrettd7655fr4n2y/node_modules/next/dist/esm/server/web/spec-extension/cookies.js // CONCATENATED MODULE: ./node_modules/.pnpm/file+..+main-repo+packages+next+next-packed.tgz_biqbaboplfbrettd7655fr4n2y/node_modules/next/dist/esm/server/web/spec-extension/request.js
+      // EXTERNAL MODULE: ./node_modules/.pnpm/file+..+diff-repo+packages+next+next-packed.tgz_biqbaboplfbrettd7655fr4n2y/node_modules/next/dist/compiled/@edge-runtime/cookies/index.js
+      var cookies = __webpack_require__(2597); // CONCATENATED MODULE: ./node_modules/.pnpm/file+..+diff-repo+packages+next+next-packed.tgz_biqbaboplfbrettd7655fr4n2y/node_modules/next/dist/esm/server/web/spec-extension/cookies.js // CONCATENATED MODULE: ./node_modules/.pnpm/file+..+diff-repo+packages+next+next-packed.tgz_biqbaboplfbrettd7655fr4n2y/node_modules/next/dist/esm/server/web/spec-extension/request.js
       //# sourceMappingURL=cookies.js.map
 
       const INTERNALS = Symbol("internal request");
@@ -884,7 +884,7 @@ Learn More: https://nextjs.org/docs/messages/node-module-in-edge-runtime`;
         get url() {
           return this[INTERNALS].url;
         }
-      } //# sourceMappingURL=request.js.map // CONCATENATED MODULE: ./node_modules/.pnpm/file+..+main-repo+packages+next+next-packed.tgz_biqbaboplfbrettd7655fr4n2y/node_modules/next/dist/esm/server/web/spec-extension/response.js
+      } //# sourceMappingURL=request.js.map // CONCATENATED MODULE: ./node_modules/.pnpm/file+..+diff-repo+packages+next+next-packed.tgz_biqbaboplfbrettd7655fr4n2y/node_modules/next/dist/esm/server/web/spec-extension/response.js
 
       const response_INTERNALS = Symbol("internal response");
       const REDIRECTS = new Set([301, 302, 303, 307, 308]);
@@ -983,7 +983,7 @@ Learn More: https://nextjs.org/docs/messages/node-module-in-edge-runtime`;
             headers,
           });
         }
-      } //# sourceMappingURL=response.js.map // CONCATENATED MODULE: ./node_modules/.pnpm/file+..+main-repo+packages+next+next-packed.tgz_biqbaboplfbrettd7655fr4n2y/node_modules/next/dist/esm/shared/lib/router/utils/relativize-url.js
+      } //# sourceMappingURL=response.js.map // CONCATENATED MODULE: ./node_modules/.pnpm/file+..+diff-repo+packages+next+next-packed.tgz_biqbaboplfbrettd7655fr4n2y/node_modules/next/dist/esm/shared/lib/router/utils/relativize-url.js
 
       /**
        * Given a URL as a string and a base URL it will make the URL relative
@@ -996,7 +996,7 @@ Learn More: https://nextjs.org/docs/messages/node-module-in-edge-runtime`;
         return relative.protocol + "//" + relative.host === origin
           ? relative.toString().replace(origin, "")
           : relative.toString();
-      } //# sourceMappingURL=relativize-url.js.map // CONCATENATED MODULE: ./node_modules/.pnpm/file+..+main-repo+packages+next+next-packed.tgz_biqbaboplfbrettd7655fr4n2y/node_modules/next/dist/esm/client/components/app-router-headers.js
+      } //# sourceMappingURL=relativize-url.js.map // CONCATENATED MODULE: ./node_modules/.pnpm/file+..+diff-repo+packages+next+next-packed.tgz_biqbaboplfbrettd7655fr4n2y/node_modules/next/dist/esm/client/components/app-router-headers.js
 
       const RSC = "RSC";
       const ACTION = "Next-Action";
@@ -1018,7 +1018,7 @@ Learn More: https://nextjs.org/docs/messages/node-module-in-edge-runtime`;
         [NEXT_ROUTER_STATE_TREE],
         [NEXT_ROUTER_PREFETCH],
       ];
-      const NEXT_RSC_UNION_QUERY = "_rsc"; //# sourceMappingURL=app-router-headers.js.map // CONCATENATED MODULE: ./node_modules/.pnpm/file+..+main-repo+packages+next+next-packed.tgz_biqbaboplfbrettd7655fr4n2y/node_modules/next/dist/esm/server/internal-utils.js
+      const NEXT_RSC_UNION_QUERY = "_rsc"; //# sourceMappingURL=app-router-headers.js.map // CONCATENATED MODULE: ./node_modules/.pnpm/file+..+diff-repo+packages+next+next-packed.tgz_biqbaboplfbrettd7655fr4n2y/node_modules/next/dist/esm/server/internal-utils.js
 
       const INTERNAL_QUERY_NAMES = [
         "__nextFallback",
@@ -1046,7 +1046,7 @@ Learn More: https://nextjs.org/docs/messages/node-module-in-edge-runtime`;
           }
         }
         return isStringUrl ? instance.toString() : instance;
-      } //# sourceMappingURL=internal-utils.js.map // CONCATENATED MODULE: ./node_modules/.pnpm/file+..+main-repo+packages+next+next-packed.tgz_biqbaboplfbrettd7655fr4n2y/node_modules/next/dist/esm/shared/lib/router/utils/app-paths.js
+      } //# sourceMappingURL=internal-utils.js.map // CONCATENATED MODULE: ./node_modules/.pnpm/file+..+diff-repo+packages+next+next-packed.tgz_biqbaboplfbrettd7655fr4n2y/node_modules/next/dist/esm/shared/lib/router/utils/app-paths.js
 
       /**
        * Normalizes an app route so it represents the actual request path. Essentially
@@ -1097,7 +1097,7 @@ Learn More: https://nextjs.org/docs/messages/node-module-in-edge-runtime`;
        * Since this function is used on full urls it checks `?` for searchParams handling.
        */ function normalizeRscPath(pathname, enabled) {
         return enabled ? pathname.replace(/\.rsc($|\?)/, "$1") : pathname;
-      } //# sourceMappingURL=app-paths.js.map // CONCATENATED MODULE: ./node_modules/.pnpm/file+..+main-repo+packages+next+next-packed.tgz_biqbaboplfbrettd7655fr4n2y/node_modules/next/dist/esm/lib/constants.js
+      } //# sourceMappingURL=app-paths.js.map // CONCATENATED MODULE: ./node_modules/.pnpm/file+..+diff-repo+packages+next+next-packed.tgz_biqbaboplfbrettd7655fr4n2y/node_modules/next/dist/esm/lib/constants.js
 
       const NEXT_QUERY_PARAM_PREFIX = "nxtP";
       const PRERENDER_REVALIDATE_HEADER = "x-prerender-revalidate";
@@ -1235,7 +1235,7 @@ Learn More: https://nextjs.org/docs/messages/node-module-in-edge-runtime`;
         metadata: "__next_metadata__",
         metadataRoute: "__next_metadata_route__",
         metadataImageMeta: "__next_metadata_image_meta__",
-      }; //# sourceMappingURL=constants.js.map // CONCATENATED MODULE: ./node_modules/.pnpm/file+..+main-repo+packages+next+next-packed.tgz_biqbaboplfbrettd7655fr4n2y/node_modules/next/dist/esm/server/web/adapter.js
+      }; //# sourceMappingURL=constants.js.map // CONCATENATED MODULE: ./node_modules/.pnpm/file+..+diff-repo+packages+next+next-packed.tgz_biqbaboplfbrettd7655fr4n2y/node_modules/next/dist/esm/server/web/adapter.js
 
       class NextRequestHint extends NextRequest {
         constructor(params) {
@@ -1464,14 +1464,14 @@ Learn More: https://nextjs.org/docs/messages/node-module-in-edge-runtime`;
           response: finalResponse,
           waitUntil: Promise.all(event[waitUntilSymbol]),
         };
-      } //# sourceMappingURL=adapter.js.map // CONCATENATED MODULE: ./node_modules/.pnpm/file+..+main-repo+packages+next+next-packed.tgz_biqbaboplfbrettd7655fr4n2y/node_modules/next/dist/esm/server/web/exports/next-response.js // CONCATENATED MODULE: ./middleware.js
+      } //# sourceMappingURL=adapter.js.map // CONCATENATED MODULE: ./node_modules/.pnpm/file+..+diff-repo+packages+next+next-packed.tgz_biqbaboplfbrettd7655fr4n2y/node_modules/next/dist/esm/server/web/exports/next-response.js // CONCATENATED MODULE: ./middleware.js
 
       // This file is for modularized imports for next/server to get fully-treeshaking.
       //# sourceMappingURL=next-response.js.map
 
       async function middleware() {
         return NextResponse.next();
-      } // CONCATENATED MODULE: ./node_modules/.pnpm/file+..+main-repo+packages+next+next-packed.tgz_biqbaboplfbrettd7655fr4n2y/node_modules/next/dist/build/webpack/loaders/next-middleware-loader.js?absolutePagePath=private-next-root-dir%2Fmiddleware.js&page=%2Fmiddleware&rootDir=%2Ftmp%2Fnext-statsAViw3K%2Fstats-app&matchers=&preferredRegion=&middlewareConfig=e30%3D!
+      } // CONCATENATED MODULE: ./node_modules/.pnpm/file+..+diff-repo+packages+next+next-packed.tgz_biqbaboplfbrettd7655fr4n2y/node_modules/next/dist/build/webpack/loaders/next-middleware-loader.js?absolutePagePath=private-next-root-dir%2Fmiddleware.js&page=%2Fmiddleware&rootDir=%2Ftmp%2Fnext-statsAViw3K%2Fstats-app&matchers=&preferredRegion=&middlewareConfig=e30%3D!
 
       const mod = { ...middleware_namespaceObject };
       const handler = mod.middleware || mod.default;
@@ -1495,7 +1495,7 @@ Learn More: https://nextjs.org/docs/messages/node-module-in-edge-runtime`;
       /***/
     },
 
-    /***/ 7238: /***/ (module) => {
+    /***/ 2597: /***/ (module) => {
       "use strict";
 
       var __defProp = Object.defineProperty;
@@ -1892,7 +1892,7 @@ Learn More: https://nextjs.org/docs/messages/node-module-in-edge-runtime`;
     // webpackRuntimeModules
     /******/ var __webpack_exec__ = (moduleId) =>
       __webpack_require__((__webpack_require__.s = moduleId));
-    /******/ var __webpack_exports__ = __webpack_exec__(2551);
+    /******/ var __webpack_exports__ = __webpack_exec__(3353);
     /******/ (_ENTRIES =
       typeof _ENTRIES === "undefined" ? {} : _ENTRIES).middleware_middleware =
       __webpack_exports__;
Diff for edge-ssr.js

Diff too large to display

Diff for _buildManifest.js
@@ -1,28 +1,28 @@
 self.__BUILD_MANIFEST = {
   __rewrites: { afterFiles: [], beforeFiles: [], fallback: [] },
-  "/": ["static\u002Fchunks\u002Fpages\u002Findex-b3eeca82dc15d3ca.js"],
-  "/_error": ["static\u002Fchunks\u002Fpages\u002F_error-95fd436e01e410ce.js"],
-  "/amp": ["static\u002Fchunks\u002Fpages\u002Famp-f480a7f42402bc6b.js"],
+  "/": ["static\u002Fchunks\u002Fpages\u002Findex-94016d70d7482fe7.js"],
+  "/_error": ["static\u002Fchunks\u002Fpages\u002F_error-a56f0e852cd19c98.js"],
+  "/amp": ["static\u002Fchunks\u002Fpages\u002Famp-8c903ea74383c3c2.js"],
   "/css": [
     "static\u002Fcss\u002Fded6b86ab9cc0a1f.css",
-    "static\u002Fchunks\u002Fpages\u002Fcss-6cba698e124cc650.js",
+    "static\u002Fchunks\u002Fpages\u002Fcss-0d504051a85f0fe7.js",
   ],
   "/dynamic": [
-    "static\u002Fchunks\u002Fpages\u002Fdynamic-1cd0163617ec6914.js",
+    "static\u002Fchunks\u002Fpages\u002Fdynamic-c49fc16237dca2a5.js",
   ],
   "/edge-ssr": [
-    "static\u002Fchunks\u002Fpages\u002Fedge-ssr-845a7224bdb4411b.js",
+    "static\u002Fchunks\u002Fpages\u002Fedge-ssr-5745a64fa421cb05.js",
   ],
-  "/head": ["static\u002Fchunks\u002Fpages\u002Fhead-542d60b8442e93ee.js"],
-  "/hooks": ["static\u002Fchunks\u002Fpages\u002Fhooks-e82577e955d3ddd0.js"],
-  "/image": ["static\u002Fchunks\u002Fpages\u002Fimage-6bb5b6e47660a843.js"],
-  "/link": ["static\u002Fchunks\u002Fpages\u002Flink-c0ec4cd09f99730f.js"],
+  "/head": ["static\u002Fchunks\u002Fpages\u002Fhead-d93013a29556304c.js"],
+  "/hooks": ["static\u002Fchunks\u002Fpages\u002Fhooks-78c139c1b3a7077d.js"],
+  "/image": ["static\u002Fchunks\u002Fpages\u002Fimage-11c16bf6265369a7.js"],
+  "/link": ["static\u002Fchunks\u002Fpages\u002Flink-07c1abec25dfa204.js"],
   "/routerDirect": [
-    "static\u002Fchunks\u002Fpages\u002FrouterDirect-a4378fdd445a6dba.js",
+    "static\u002Fchunks\u002Fpages\u002FrouterDirect-21980ec88959c07d.js",
   ],
-  "/script": ["static\u002Fchunks\u002Fpages\u002Fscript-0c22bdc31d7a54f9.js"],
+  "/script": ["static\u002Fchunks\u002Fpages\u002Fscript-4ac368a5fe7f6515.js"],
   "/withRouter": [
-    "static\u002Fchunks\u002Fpages\u002FwithRouter-1c13d6b4c43864b1.js",
+    "static\u002Fchunks\u002Fpages\u002FwithRouter-cd6c8ed384d35879.js",
   ],
   sortedPages: [
     "\u002F",
Diff for _app-HASH.js
@@ -1,7 +1,7 @@
 (self["webpackChunk_N_E"] = self["webpackChunk_N_E"] || []).push([
   [888],
   {
-    /***/ 2190: /***/ function (
+    /***/ 1113: /***/ function (
       __unused_webpack_module,
       __unused_webpack_exports,
       __webpack_require__
@@ -9,7 +9,7 @@
       (window.__NEXT_P = window.__NEXT_P || []).push([
         "/_app",
         function () {
-          return __webpack_require__(783);
+          return __webpack_require__(6355);
         },
       ]);
       if (false) {
@@ -24,7 +24,7 @@
       return __webpack_require__((__webpack_require__.s = moduleId));
     };
     /******/ __webpack_require__.O(0, [774, 179], function () {
-      return __webpack_exec__(2190), __webpack_exec__(5761);
+      return __webpack_exec__(1113), __webpack_exec__(3103);
     });
     /******/ var __webpack_exports__ = __webpack_require__.O();
     /******/ _N_E = __webpack_exports__;
Diff for _error-HASH.js
@@ -1,7 +1,7 @@
 (self["webpackChunk_N_E"] = self["webpackChunk_N_E"] || []).push([
   [820],
   {
-    /***/ 5374: /***/ function (
+    /***/ 3363: /***/ function (
       __unused_webpack_module,
       __unused_webpack_exports,
       __webpack_require__
@@ -9,7 +9,7 @@
       (window.__NEXT_P = window.__NEXT_P || []).push([
         "/_error",
         function () {
-          return __webpack_require__(4218);
+          return __webpack_require__(6178);
         },
       ]);
       if (false) {
@@ -24,7 +24,7 @@
       return __webpack_require__((__webpack_require__.s = moduleId));
     };
     /******/ __webpack_require__.O(0, [888, 774, 179], function () {
-      return __webpack_exec__(5374);
+      return __webpack_exec__(3363);
     });
     /******/ var __webpack_exports__ = __webpack_require__.O();
     /******/ _N_E = __webpack_exports__;
Diff for amp-HASH.js
@@ -1,17 +1,17 @@
 (self["webpackChunk_N_E"] = self["webpackChunk_N_E"] || []).push([
   [216],
   {
-    /***/ 8510: /***/ function (
+    /***/ 8753: /***/ function (
       module,
       __unused_webpack_exports,
       __webpack_require__
     ) {
-      module.exports = __webpack_require__(8170);
+      module.exports = __webpack_require__(811);
 
       /***/
     },
 
-    /***/ 7010: /***/ function (
+    /***/ 5348: /***/ function (
       __unused_webpack_module,
       __unused_webpack_exports,
       __webpack_require__
@@ -19,7 +19,7 @@
       (window.__NEXT_P = window.__NEXT_P || []).push([
         "/amp",
         function () {
-          return __webpack_require__(2583);
+          return __webpack_require__(7226);
         },
       ]);
       if (false) {
@@ -28,7 +28,7 @@
       /***/
     },
 
-    /***/ 8170: /***/ function (module, exports, __webpack_require__) {
+    /***/ 811: /***/ function (module, exports, __webpack_require__) {
       "use strict";
 
       Object.defineProperty(exports, "__esModule", {
@@ -44,8 +44,8 @@
       const _react = /*#__PURE__*/ _interop_require_default._(
         __webpack_require__(959)
       );
-      const _ampcontext = __webpack_require__(4428);
-      const _ampmode = __webpack_require__(9404);
+      const _ampcontext = __webpack_require__(1911);
+      const _ampmode = __webpack_require__(8505);
       function useAmp() {
         // Don't assign the context value to a variable to save bytes
         return (0, _ampmode.isInAmpMode)(
@@ -67,7 +67,7 @@
       /***/
     },
 
-    /***/ 2583: /***/ function (
+    /***/ 7226: /***/ function (
       __unused_webpack_module,
       __webpack_exports__,
       __webpack_require__
@@ -84,7 +84,7 @@
         /* harmony export */
       });
       /* harmony import */ var next_amp__WEBPACK_IMPORTED_MODULE_0__ =
-        __webpack_require__(8510);
+        __webpack_require__(8753);
       /* harmony import */ var next_amp__WEBPACK_IMPORTED_MODULE_0___default =
         /*#__PURE__*/ __webpack_require__.n(
           next_amp__WEBPACK_IMPORTED_MODULE_0__
@@ -108,7 +108,7 @@
       return __webpack_require__((__webpack_require__.s = moduleId));
     };
     /******/ __webpack_require__.O(0, [888, 774, 179], function () {
-      return __webpack_exec__(7010);
+      return __webpack_exec__(5348);
     });
     /******/ var __webpack_exports__ = __webpack_require__.O();
     /******/ _N_E = __webpack_exports__;
Diff for css-HASH.js
@@ -1,7 +1,7 @@
 (self["webpackChunk_N_E"] = self["webpackChunk_N_E"] || []).push([
   [706],
   {
-    /***/ 860: /***/ function (
+    /***/ 6437: /***/ function (
       __unused_webpack_module,
       __unused_webpack_exports,
       __webpack_require__
@@ -9,7 +9,7 @@
       (window.__NEXT_P = window.__NEXT_P || []).push([
         "/css",
         function () {
-          return __webpack_require__(3511);
+          return __webpack_require__(7123);
         },
       ]);
       if (false) {
@@ -18,7 +18,7 @@
       /***/
     },
 
-    /***/ 3511: /***/ function (
+    /***/ 7123: /***/ function (
       __unused_webpack_module,
       __webpack_exports__,
       __webpack_require__
@@ -28,7 +28,7 @@
       /* harmony import */ var react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__ =
         __webpack_require__(1527);
       /* harmony import */ var _css_module_css__WEBPACK_IMPORTED_MODULE_1__ =
-        __webpack_require__(4107);
+        __webpack_require__(3234);
       /* harmony import */ var _css_module_css__WEBPACK_IMPORTED_MODULE_1___default =
         /*#__PURE__*/ __webpack_require__.n(
           _css_module_css__WEBPACK_IMPORTED_MODULE_1__
@@ -48,7 +48,7 @@
       /***/
     },
 
-    /***/ 4107: /***/ function (module) {
+    /***/ 3234: /***/ function (module) {
       // extracted by mini-css-extract-plugin
       module.exports = { helloWorld: "css_helloWorld__aUdUq" };
 
@@ -61,7 +61,7 @@
       return __webpack_require__((__webpack_require__.s = moduleId));
     };
     /******/ __webpack_require__.O(0, [774, 888, 179], function () {
-      return __webpack_exec__(860);
+      return __webpack_exec__(6437);
     });
     /******/ var __webpack_exports__ = __webpack_require__.O();
     /******/ _N_E = __webpack_exports__;
Diff for dynamic-HASH.js
@@ -1,7 +1,7 @@
 (self["webpackChunk_N_E"] = self["webpackChunk_N_E"] || []).push([
   [739],
   {
-    /***/ 3995: /***/ function (
+    /***/ 7753: /***/ function (
       __unused_webpack_module,
       __unused_webpack_exports,
       __webpack_require__
@@ -9,7 +9,7 @@
       (window.__NEXT_P = window.__NEXT_P || []).push([
         "/dynamic",
         function () {
-          return __webpack_require__(8661);
+          return __webpack_require__(3245);
         },
       ]);
       if (false) {
@@ -18,7 +18,7 @@
       /***/
     },
 
-    /***/ 2831: /***/ function (module, exports, __webpack_require__) {
+    /***/ 4727: /***/ function (module, exports, __webpack_require__) {
       "use strict";
 
       Object.defineProperty(exports, "__esModule", {
@@ -45,7 +45,7 @@
         __webpack_require__(959)
       );
       const _loadable = /*#__PURE__*/ _interop_require_default._(
-        __webpack_require__(8539)
+        __webpack_require__(7666)
       );
       const isServerSide = "object" === "undefined";
       // Normalize loader to return the module as form { default: Component } for `React.lazy`.
@@ -146,7 +146,7 @@
       /***/
     },
 
-    /***/ 5242: /***/ function (
+    /***/ 1591: /***/ function (
       __unused_webpack_module,
       exports,
       __webpack_require__
@@ -173,7 +173,7 @@
       /***/
     },
 
-    /***/ 8539: /***/ function (
+    /***/ 7666: /***/ function (
       __unused_webpack_module,
       exports,
       __webpack_require__
@@ -215,7 +215,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE
       const _react = /*#__PURE__*/ _interop_require_default._(
         __webpack_require__(959)
       );
-      const _loadablecontext = __webpack_require__(5242);
+      const _loadablecontext = __webpack_require__(1591);
       function resolve(obj) {
         return obj && obj.default ? obj.default : obj;
       }
@@ -450,7 +450,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE
       /***/
     },
 
-    /***/ 8661: /***/ function (
+    /***/ 3245: /***/ function (
       __unused_webpack_module,
       __webpack_exports__,
       __webpack_require__
@@ -466,7 +466,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE
       /* harmony import */ var react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__ =
         __webpack_require__(1527);
       /* harmony import */ var next_dynamic__WEBPACK_IMPORTED_MODULE_1__ =
-        __webpack_require__(6113);
+        __webpack_require__(2677);
       /* harmony import */ var next_dynamic__WEBPACK_IMPORTED_MODULE_1___default =
         /*#__PURE__*/ __webpack_require__.n(
           next_dynamic__WEBPACK_IMPORTED_MODULE_1__
@@ -475,11 +475,11 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE
       const DynamicHello = next_dynamic__WEBPACK_IMPORTED_MODULE_1___default()(
         () =>
           __webpack_require__
-            .e(/* import() */ 446)
-            .then(__webpack_require__.bind(__webpack_require__, 3446)),
+            .e(/* import() */ 574)
+            .then(__webpack_require__.bind(__webpack_require__, 8574)),
         {
           loadableGenerated: {
-            webpack: () => [/*require.resolve*/ 3446],
+            webpack: () => [/*require.resolve*/ 8574],
           },
         }
       );
@@ -506,12 +506,12 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE
       /***/
     },
 
-    /***/ 6113: /***/ function (
+    /***/ 2677: /***/ function (
       module,
       __unused_webpack_exports,
       __webpack_require__
     ) {
-      module.exports = __webpack_require__(2831);
+      module.exports = __webpack_require__(4727);
 
       /***/
     },
@@ -522,7 +522,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE
       return __webpack_require__((__webpack_require__.s = moduleId));
     };
     /******/ __webpack_require__.O(0, [774, 888, 179], function () {
-      return __webpack_exec__(3995);
+      return __webpack_exec__(7753);
     });
     /******/ var __webpack_exports__ = __webpack_require__.O();
     /******/ _N_E = __webpack_exports__;
Diff for edge-ssr-HASH.js
@@ -1,7 +1,7 @@
 (self["webpackChunk_N_E"] = self["webpackChunk_N_E"] || []).push([
   [800],
   {
-    /***/ 6219: /***/ function (
+    /***/ 6027: /***/ function (
       __unused_webpack_module,
       __unused_webpack_exports,
       __webpack_require__
@@ -9,7 +9,7 @@
       (window.__NEXT_P = window.__NEXT_P || []).push([
         "/edge-ssr",
         function () {
-          return __webpack_require__(2230);
+          return __webpack_require__(9170);
         },
       ]);
       if (false) {
@@ -18,7 +18,7 @@
       /***/
     },
 
-    /***/ 2230: /***/ function (
+    /***/ 9170: /***/ function (
       __unused_webpack_module,
       __webpack_exports__,
       __webpack_require__
@@ -48,7 +48,7 @@
       return __webpack_require__((__webpack_require__.s = moduleId));
     };
     /******/ __webpack_require__.O(0, [888, 774, 179], function () {
-      return __webpack_exec__(6219);
+      return __webpack_exec__(6027);
     });
     /******/ var __webpack_exports__ = __webpack_require__.O();
     /******/ _N_E = __webpack_exports__;
Diff for head-HASH.js
@@ -1,7 +1,7 @@
 (self["webpackChunk_N_E"] = self["webpackChunk_N_E"] || []).push([
   [645],
   {
-    /***/ 2899: /***/ function (
+    /***/ 6955: /***/ function (
       __unused_webpack_module,
       __unused_webpack_exports,
       __webpack_require__
@@ -9,7 +9,7 @@
       (window.__NEXT_P = window.__NEXT_P || []).push([
         "/head",
         function () {
-          return __webpack_require__(4202);
+          return __webpack_require__(792);
         },
       ]);
       if (false) {
@@ -18,7 +18,7 @@
       /***/
     },
 
-    /***/ 4202: /***/ function (
+    /***/ 792: /***/ function (
       __unused_webpack_module,
       __webpack_exports__,
       __webpack_require__
@@ -34,7 +34,7 @@
       /* harmony import */ var react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__ =
         __webpack_require__(1527);
       /* harmony import */ var next_head__WEBPACK_IMPORTED_MODULE_1__ =
-        __webpack_require__(8412);
+        __webpack_require__(9046);
       /* harmony import */ var next_head__WEBPACK_IMPORTED_MODULE_1___default =
         /*#__PURE__*/ __webpack_require__.n(
           next_head__WEBPACK_IMPORTED_MODULE_1__
@@ -68,12 +68,12 @@
       /***/
     },
 
-    /***/ 8412: /***/ function (
+    /***/ 9046: /***/ function (
       module,
       __unused_webpack_exports,
       __webpack_require__
     ) {
-      module.exports = __webpack_require__(3373);
+      module.exports = __webpack_require__(2285);
 
       /***/
     },
@@ -84,7 +84,7 @@
       return __webpack_require__((__webpack_require__.s = moduleId));
     };
     /******/ __webpack_require__.O(0, [774, 888, 179], function () {
-      return __webpack_exec__(2899);
+      return __webpack_exec__(6955);
     });
     /******/ var __webpack_exports__ = __webpack_require__.O();
     /******/ _N_E = __webpack_exports__;
Diff for hooks-HASH.js
@@ -1,7 +1,7 @@
 (self["webpackChunk_N_E"] = self["webpackChunk_N_E"] || []).push([
   [757],
   {
-    /***/ 2304: /***/ function (
+    /***/ 7819: /***/ function (
       __unused_webpack_module,
       __unused_webpack_exports,
       __webpack_require__
@@ -9,7 +9,7 @@
       (window.__NEXT_P = window.__NEXT_P || []).push([
         "/hooks",
         function () {
-          return __webpack_require__(1716);
+          return __webpack_require__(3898);
         },
       ]);
       if (false) {
@@ -18,7 +18,7 @@
       /***/
     },
 
-    /***/ 1716: /***/ function (
+    /***/ 3898: /***/ function (
       __unused_webpack_module,
       __webpack_exports__,
       __webpack_require__
@@ -79,7 +79,7 @@
       return __webpack_require__((__webpack_require__.s = moduleId));
     };
     /******/ __webpack_require__.O(0, [774, 888, 179], function () {
-      return __webpack_exec__(2304);
+      return __webpack_exec__(7819);
     });
     /******/ var __webpack_exports__ = __webpack_require__.O();
     /******/ _N_E = __webpack_exports__;
Diff for image-HASH.js
@@ -1,7 +1,7 @@
 (self["webpackChunk_N_E"] = self["webpackChunk_N_E"] || []).push([
   [358],
   {
-    /***/ 5885: /***/ function (
+    /***/ 801: /***/ function (
       __unused_webpack_module,
       __unused_webpack_exports,
       __webpack_require__
@@ -9,7 +9,7 @@
       (window.__NEXT_P = window.__NEXT_P || []).push([
         "/image",
         function () {
-          return __webpack_require__(7141);
+          return __webpack_require__(7330);
         },
       ]);
       if (false) {
@@ -18,7 +18,7 @@
       /***/
     },
 
-    /***/ 645: /***/ function (module, exports, __webpack_require__) {
+    /***/ 3727: /***/ function (module, exports, __webpack_require__) {
       "use strict";
       /* __next_internal_client_entry_do_not_use__  cjs */
       Object.defineProperty(exports, "__esModule", {
@@ -37,15 +37,15 @@
       );
       const _reactdom = __webpack_require__(422);
       const _head = /*#__PURE__*/ _interop_require_default._(
-        __webpack_require__(3373)
+        __webpack_require__(2285)
       );
-      const _getimgprops = __webpack_require__(9671);
-      const _imageconfig = __webpack_require__(7497);
-      const _imageconfigcontext = __webpack_require__(4281);
-      const _warnonce = __webpack_require__(130);
-      const _routercontext = __webpack_require__(4700);
+      const _getimgprops = __webpack_require__(4813);
+      const _imageconfig = __webpack_require__(6330);
+      const _imageconfigcontext = __webpack_require__(4883);
+      const _warnonce = __webpack_require__(114);
+      const _routercontext = __webpack_require__(4918);
       const _imageloader = /*#__PURE__*/ _interop_require_default._(
-        __webpack_require__(3545)
+        __webpack_require__(4317)
       );
       // This is replaced by webpack define plugin
       const configEnv = {
@@ -370,7 +370,7 @@
       /***/
     },
 
-    /***/ 9671: /***/ function (
+    /***/ 4813: /***/ function (
       __unused_webpack_module,
       exports,
       __webpack_require__
@@ -386,9 +386,9 @@
           return getImgProps;
         },
       });
-      const _warnonce = __webpack_require__(130);
-      const _imageblursvg = __webpack_require__(4890);
-      const _imageconfig = __webpack_require__(7497);
+      const _warnonce = __webpack_require__(114);
+      const _imageblursvg = __webpack_require__(7318);
+      const _imageconfig = __webpack_require__(6330);
       const VALID_LOADING_VALUES =
         /* unused pure expression or super */ null && [
           "lazy",
@@ -753,7 +753,7 @@
       /***/
     },
 
-    /***/ 4890: /***/ function (__unused_webpack_module, exports) {
+    /***/ 7318: /***/ function (__unused_webpack_module, exports) {
       "use strict";
       /**
        * A shared function, used on both client and server, to generate a SVG blur placeholder.
@@ -776,37 +776,30 @@
           blurDataURL,
           objectFit,
         } = param;
-        const std = blurWidth && blurHeight ? "1" : "20";
-        const svgWidth = blurWidth || widthInt;
-        const svgHeight = blurHeight || heightInt;
-        const feComponentTransfer = blurDataURL.startsWith("data:image/jpeg")
-          ? "%3CfeComponentTransfer%3E%3CfeFuncA type='discrete' tableValues='1 1'/%3E%3C/feComponentTransfer%3E%"
-          : "";
-        if (svgWidth && svgHeight) {
-          return (
-            "%3Csvg xmlns='http%3A//www.w3.org/2000/svg' viewBox='0 0 " +
-            svgWidth +
-            " " +
-            svgHeight +
-            "'%3E%3Cfilter id='b' color-interpolation-filters='sRGB'%3E%3CfeGaussianBlur stdDeviation='" +
-            std +
-            "'/%3E" +
-            feComponentTransfer +
-            "%3C/filter%3E%3Cimage preserveAspectRatio='none' filter='url(%23b)' x='0' y='0' height='100%25' width='100%25' href='" +
-            blurDataURL +
-            "'/%3E%3C/svg%3E"
-          );
-        }
-        const preserveAspectRatio =
-          objectFit === "contain"
-            ? "xMidYMid"
-            : objectFit === "cover"
-            ? "xMidYMid slice"
-            : "none";
+        const std = 20;
+        const svgWidth = blurWidth ? blurWidth * 40 : widthInt;
+        const svgHeight = blurHeight ? blurHeight * 40 : heightInt;
+        const viewBox =
+          svgWidth && svgHeight
+            ? "viewBox='0 0 " + svgWidth + " " + svgHeight + "'"
+            : "";
+        const preserveAspectRatio = viewBox
+          ? "none"
+          : objectFit === "contain"
+          ? "xMidYMid"
+          : objectFit === "cover"
+          ? "xMidYMid slice"
+          : "none";
         return (
-          "%3Csvg xmlns='http%3A//www.w3.org/2000/svg'%3E%3Cimage style='filter:blur(20px)' preserveAspectRatio='" +
+          "%3Csvg xmlns='http://www.w3.org/2000/svg' " +
+          viewBox +
+          "%3E%3Cfilter id='b' color-interpolation-filters='sRGB'%3E%3CfeGaussianBlur stdDeviation='" +
+          std +
+          "'/%3E%3CfeColorMatrix values='1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 100 -1' result='s'/%3E%3CfeFlood x='0' y='0' width='100%25' height='100%25'/%3E%3CfeComposite operator='out' in='s'/%3E%3CfeComposite in2='SourceGraphic'/%3E%3CfeGaussianBlur stdDeviation='" +
+          std +
+          "'/%3E%3C/filter%3E%3Cimage width='100%25' height='100%25' x='0' y='0' preserveAspectRatio='" +
           preserveAspectRatio +
-          "' x='0' y='0' height='100%25' width='100%25' href='" +
+          "' style='filter: url(%23b);' href='" +
           blurDataURL +
           "'/%3E%3C/svg%3E"
         );
@@ -815,7 +808,7 @@
       /***/
     },
 
-    /***/ 4971: /***/ function (
+    /***/ 4741: /***/ function (
       __unused_webpack_module,
       exports,
       __webpack_require__
@@ -842,11 +835,11 @@
         },
       });
       const _interop_require_default = __webpack_require__(8266);
-      const _getimgprops = __webpack_require__(9671);
-      const _warnonce = __webpack_require__(130);
-      const _imagecomponent = __webpack_require__(645);
+      const _getimgprops = __webpack_require__(4813);
+      const _warnonce = __webpack_require__(114);
+      const _imagecomponent = __webpack_require__(3727);
       const _imageloader = /*#__PURE__*/ _interop_require_default._(
-        __webpack_require__(3545)
+        __webpack_require__(4317)
       );
       const unstable_getImgProps = (imgProps) => {
         (0, _warnonce.warnOnce)(
@@ -878,7 +871,7 @@
       /***/
     },
 
-    /***/ 3545: /***/ function (__unused_webpack_module, exports) {
+    /***/ 4317: /***/ function (__unused_webpack_module, exports) {
       "use strict";
 
       Object.defineProperty(exports, "__esModule", {
@@ -913,7 +906,7 @@
       /***/
     },
 
-    /***/ 7141: /***/ function (
+    /***/ 7330: /***/ function (
       __unused_webpack_module,
       __webpack_exports__,
       __webpack_require__
@@ -934,8 +927,8 @@
 
       // EXTERNAL MODULE: ./node_modules/.pnpm/react@18.2.0/node_modules/react/jsx-runtime.js
       var jsx_runtime = __webpack_require__(1527);
-      // EXTERNAL MODULE: ./node_modules/.pnpm/file+..+main-repo+packages+next+next-packed.tgz_biqbaboplfbrettd7655fr4n2y/node_modules/next/image.js
-      var next_image = __webpack_require__(3960);
+      // EXTERNAL MODULE: ./node_modules/.pnpm/file+..+diff-repo+packages+next+next-packed.tgz_biqbaboplfbrettd7655fr4n2y/node_modules/next/image.js
+      var next_image = __webpack_require__(8249);
       var image_default = /*#__PURE__*/ __webpack_require__.n(next_image); // CONCATENATED MODULE: ./pages/nextjs.png
       /* harmony default export */ var nextjs = {
         src: "/_next/static/media/nextjs.cae0b805.png",
@@ -965,12 +958,12 @@
       /***/
     },
 
-    /***/ 3960: /***/ function (
+    /***/ 8249: /***/ function (
       module,
       __unused_webpack_exports,
       __webpack_require__
     ) {
-      module.exports = __webpack_require__(4971);
+      module.exports = __webpack_require__(4741);
 
       /***/
     },
@@ -981,7 +974,7 @@
       return __webpack_require__((__webpack_require__.s = moduleId));
     };
     /******/ __webpack_require__.O(0, [774, 888, 179], function () {
-      return __webpack_exec__(5885);
+      return __webpack_exec__(801);
     });
     /******/ var __webpack_exports__ = __webpack_require__.O();
     /******/ _N_E = __webpack_exports__;
Diff for index-HASH.js
@@ -1,7 +1,7 @@
 (self["webpackChunk_N_E"] = self["webpackChunk_N_E"] || []).push([
   [405],
   {
-    /***/ 3558: /***/ function (
+    /***/ 7042: /***/ function (
       __unused_webpack_module,
       __unused_webpack_exports,
       __webpack_require__
@@ -9,7 +9,7 @@
       (window.__NEXT_P = window.__NEXT_P || []).push([
         "/",
         function () {
-          return __webpack_require__(8743);
+          return __webpack_require__(8933);
         },
       ]);
       if (false) {
@@ -18,7 +18,7 @@
       /***/
     },
 
-    /***/ 8743: /***/ function (
+    /***/ 8933: /***/ function (
       __unused_webpack_module,
       __webpack_exports__,
       __webpack_require__
@@ -44,7 +44,7 @@
       return __webpack_require__((__webpack_require__.s = moduleId));
     };
     /******/ __webpack_require__.O(0, [888, 774, 179], function () {
-      return __webpack_exec__(3558);
+      return __webpack_exec__(7042);
     });
     /******/ var __webpack_exports__ = __webpack_require__.O();
     /******/ _N_E = __webpack_exports__;
Diff for link-HASH.js
@@ -1,7 +1,7 @@
 (self["webpackChunk_N_E"] = self["webpackChunk_N_E"] || []).push([
   [644],
   {
-    /***/ 8662: /***/ function (
+    /***/ 7014: /***/ function (
       __unused_webpack_module,
       __unused_webpack_exports,
       __webpack_require__
@@ -9,7 +9,7 @@
       (window.__NEXT_P = window.__NEXT_P || []).push([
         "/link",
         function () {
-          return __webpack_require__(2891);
+          return __webpack_require__(4265);
         },
       ]);
       if (false) {
@@ -18,7 +18,7 @@
       /***/
     },
 
-    /***/ 2078: /***/ function (module, exports) {
+    /***/ 1258: /***/ function (module, exports) {
       "use strict";
 
       Object.defineProperty(exports, "__esModule", {
@@ -86,7 +86,7 @@
       /***/
     },
 
-    /***/ 4263: /***/ function (module, exports, __webpack_require__) {
+    /***/ 452: /***/ function (module, exports, __webpack_require__) {
       "use strict";
 
       Object.defineProperty(exports, "__esModule", {
@@ -98,7 +98,7 @@
           return getDomainLocale;
         },
       });
-      const _normalizetrailingslash = __webpack_require__(2424);
+      const _normalizetrailingslash = __webpack_require__(3537);
       const basePath =
         /* unused pure expression or super */ null && (false || "");
       function getDomainLocale(path, locale, locales, domainLocales) {
@@ -122,7 +122,7 @@
       /***/
     },
 
-    /***/ 8027: /***/ function (module, exports, __webpack_require__) {
+    /***/ 8681: /***/ function (module, exports, __webpack_require__) {
       "use strict";
       /* __next_internal_client_entry_do_not_use__  cjs */
       Object.defineProperty(exports, "__esModule", {
@@ -138,17 +138,17 @@
       const _react = /*#__PURE__*/ _interop_require_default._(
         __webpack_require__(959)
       );
-      const _resolvehref = __webpack_require__(1698);
-      const _islocalurl = __webpack_require__(5216);
-      const _formaturl = __webpack_require__(9262);
-      const _utils = __webpack_require__(7590);
-      const _addlocale = __webpack_require__(6901);
-      const _routercontext = __webpack_require__(4700);
-      const _approutercontext = __webpack_require__(7922);
-      const _useintersection = __webpack_require__(8734);
-      const _getdomainlocale = __webpack_require__(4263);
-      const _addbasepath = __webpack_require__(3841);
-      const _routerreducertypes = __webpack_require__(2078);
+      const _resolvehref = __webpack_require__(9432);
+      const _islocalurl = __webpack_require__(8867);
+      const _formaturl = __webpack_require__(1347);
+      const _utils = __webpack_require__(2005);
+      const _addlocale = __webpack_require__(6486);
+      const _routercontext = __webpack_require__(4918);
+      const _approutercontext = __webpack_require__(9750);
+      const _useintersection = __webpack_require__(253);
+      const _getdomainlocale = __webpack_require__(452);
+      const _addbasepath = __webpack_require__(2247);
+      const _routerreducertypes = __webpack_require__(1258);
       const prefetched = new Set();
       function prefetch(router, href, as, options, appOptions, isAppRouter) {
         if (false) {
@@ -567,7 +567,7 @@
       /***/
     },
 
-    /***/ 8734: /***/ function (module, exports, __webpack_require__) {
+    /***/ 253: /***/ function (module, exports, __webpack_require__) {
       "use strict";
 
       Object.defineProperty(exports, "__esModule", {
@@ -580,7 +580,7 @@
         },
       });
       const _react = __webpack_require__(959);
-      const _requestidlecallback = __webpack_require__(251);
+      const _requestidlecallback = __webpack_require__(2657);
       const hasIntersectionObserver =
         typeof IntersectionObserver === "function";
       const observers = new Map();
@@ -693,7 +693,7 @@
       /***/
     },
 
-    /***/ 2891: /***/ function (
+    /***/ 4265: /***/ function (
       __unused_webpack_module,
       __webpack_exports__,
       __webpack_require__
@@ -709,7 +709,7 @@
       /* harmony import */ var react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__ =
         __webpack_require__(1527);
       /* harmony import */ var next_link__WEBPACK_IMPORTED_MODULE_1__ =
-        __webpack_require__(4595);
+        __webpack_require__(9495);
       /* harmony import */ var next_link__WEBPACK_IMPORTED_MODULE_1___default =
         /*#__PURE__*/ __webpack_require__.n(
           next_link__WEBPACK_IMPORTED_MODULE_1__
@@ -740,12 +740,12 @@
       /***/
     },
 
-    /***/ 4595: /***/ function (
+    /***/ 9495: /***/ function (
       module,
       __unused_webpack_exports,
       __webpack_require__
     ) {
-      module.exports = __webpack_require__(8027);
+      module.exports = __webpack_require__(8681);
 
       /***/
     },
@@ -756,7 +756,7 @@
       return __webpack_require__((__webpack_require__.s = moduleId));
     };
     /******/ __webpack_require__.O(0, [774, 888, 179], function () {
-      return __webpack_exec__(8662);
+      return __webpack_exec__(7014);
     });
     /******/ var __webpack_exports__ = __webpack_require__.O();
     /******/ _N_E = __webpack_exports__;
Diff for routerDirect-H Post job cleanup. [command]/usr/bin/git version git version 2.41.0 Temporarily overriding HOME='/home/runner/work/_temp/a7a2290c-5701-460c-a124-5420250ba573' before making global git config changes Adding repository directory to the temporary git global config as a safe directory [command]/usr/bin/git config --global --add safe.directory /home/runner/work/next.js/next.js [command]/usr/bin/git config --local --name-only --get-regexp core\.sshCommand [command]/usr/bin/git submodule foreach --recursive sh -c "git config --local --name-only --get-regexp 'core\.sshCommand' && git config --local --unset-all 'core.sshCommand' || :" [command]/usr/bin/git config --local --name-only --get-regexp http\.https\:\/\/github\.com\/\.extraheader http.https://github.com/.extraheader [command]/usr/bin/git config --local --unset-all http.https://github.com/.extraheader [command]/usr/bin/git submodule foreach --recursive sh -c "git config --local --name-only --get-regexp 'http\.https\:\/\/github\.com\/\.extraheader' && git config --local --unset-all 'http.https://github.com/.extraheader' || :" Cleaning up orphan processes Commit: ce55dfd

@styfle
Copy link
Member

styfle commented Jul 12, 2023

@arturbien Something looks wrong with the id="blur-wide" image.

Try this before and after your change:

  1. pnpm next build test/integration/next-image-new/default
  2. pnpm next start test/integration/next-image-new/default
  3. Visit http://localhost:3000/static-img
  4. Look at the blur placeholder for the image with id blur-wide

Before

<img id="blur-wide" loading="lazy" width="1200" height="700" decoding="async" data-nimg="1" style="color:transparent;background-size:cover;background-position:50% 50%;background-repeat:no-repeat;background-image:url(&quot;data:image/svg+xml;charset=utf-8,%3Csvg xmlns=&#x27;http%3A//www.w3.org/2000/svg&#x27; viewBox=&#x27;0 0 8 5&#x27;%3E%3Cfilter id=&#x27;b&#x27; color-interpolation-filters=&#x27;sRGB&#x27;%3E%3CfeGaussianBlur stdDeviation=&#x27;1&#x27;/%3E%3C/filter%3E%3Cimage preserveAspectRatio=&#x27;none&#x27; filter=&#x27;url(%23b)&#x27; x=&#x27;0&#x27; y=&#x27;0&#x27; height=&#x27;100%25&#x27; width=&#x27;100%25&#x27; href=&#x27;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAFCAAAAABd+vKJAAAANklEQVR42mNg4GRwdWBgZ2BgUGI4dYhBmYFBgiHy308PBlEGKYbr//9fYJBlYDBYv3nzGkUGANGMDBq2MCnBAAAAAElFTkSuQmCC&#x27;/%3E%3C/svg%3E&quot;)" srcSet="[/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fwide.8744c79a.png&amp;w=1200&amp;q=75 1x](http://localhost:3000/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fwide.8744c79a.png&amp;w=1200&amp;q=75),[ /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fwide.8744c79a.png&amp;w=3840&amp;q=75 2x](http://localhost:3000/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fwide.8744c79a.png&amp;w=3840&amp;q=75)" src="[/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fwide.8744c79a.png&amp;w=3840&amp;q=75](http://localhost:3000/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fwide.8744c79a.png&w=3840&q=75)"/>
image

After

<img id="blur-wide" loading="lazy" width="1200" height="700" decoding="async" data-nimg="1" style="color:transparent;background-size:cover;background-position:50% 50%;background-repeat:no-repeat;background-image:url(&quot;data:image/svg+xml;charset=utf-8,%3csvg xmlns=&#x27;http://www.w3.org/2000/svg&#x27; viewBox=&#x27;0 0 8 5&#x27; width=&#x27;8&#x27; height=&#x27;5&#x27;%3e%3cfilter id=&#x27;b&#x27; color-interpolation-filters=&#x27;sRGB&#x27;%3e%3cfeMorphology in=&#x27;SourceAlpha&#x27; operator=&#x27;dilate&#x27; radius=&#x27;15&#x27; result=&#x27;dilate&#x27;%3e%3c/feMorphology%3e%3cfeGaussianBlur in=&#x27;dilate&#x27; stdDeviation=&#x27;15&#x27; result=&#x27;mask&#x27;%3e%3c/feGaussianBlur%3e%3cfeGaussianBlur in=&#x27;SourceGraphic&#x27; stdDeviation=&#x27;1&#x27; result=&#x27;blur&#x27;%3e%3c/feGaussianBlur%3e%3cfeComponentTransfer in=&#x27;blur&#x27; result=&#x27;solid&#x27;%3e%3cfeFuncA type=&#x27;table&#x27; tableValues=&#x27;1 1&#x27;%3e%3c/feFuncA%3e%3c/feComponentTransfer%3e%3cfeComposite in2=&#x27;mask&#x27; in=&#x27;solid&#x27; operator=&#x27;in&#x27; result=&#x27;comp&#x27;%3e%3c/feComposite%3e%3cfeMerge%3e%3cfeMergeNode in=&#x27;SourceGraphic&#x27;/%3e%3cfeMergeNode in=&#x27;comp&#x27;/%3e%3c/feMerge%3e%3c/filter%3e%3cimage style=&#x27;filter: url(%23b);&#x27; x=&#x27;0&#x27; y=&#x27;0&#x27; width=&#x27;100%25&#x27; height=&#x27;100%25&#x27; preserveAspectRatio=&#x27;none&#x27; href=&#x27;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAFCAAAAABd+vKJAAAANklEQVR42mNg4GRwdWBgZ2BgUGI4dYhBmYFBgiHy308PBlEGKYbr//9fYJBlYDBYv3nzGkUGANGMDBq2MCnBAAAAAElFTkSuQmCC&#x27;%3e%3c/image%3e%3c/svg%3e&quot;)" srcSet="[/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fwide.8744c79a.png&amp;w=1200&amp;q=75 1x](http://localhost:3000/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fwide.8744c79a.png&amp;w=1200&amp;q=75),[ /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fwide.8744c79a.png&amp;w=3840&amp;q=75 2x](http://localhost:3000/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fwide.8744c79a.png&amp;w=3840&amp;q=75)" src="[/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fwide.8744c79a.png&amp;w=3840&amp;q=75](http://localhost:3000/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fwide.8744c79a.png&w=3840&q=75)"/>
image

@arturbien
Copy link
Contributor Author

@styfle Looks like I didn't handle the 1px blur case properly. I'll look into this tomorrow. Thank you for your patience and sorry for taking your time!

@styfle
Copy link
Member

styfle commented Jul 12, 2023

Thanks for looking into it! There are a lot of cases to consider:

  1. When the user provides a static import: import img from './img.jpg'; <Image src={img} placeholder="blur" />
  2. When the user provides a remote image with blurDataURL: <Image src="https://example.com" width={200} height={500} placeholder="blur" blurDataURL="data:..." />
  3. When the user provides an transparent image (for src or blurDataURL), for example PNG
  4. When the user provides blurDataURL with slightly different aspect ratio then the provided width and height

@arturbien
Copy link
Contributor Author

arturbien commented Jul 24, 2023

@styfle I've pushed the changes and fixed the blur value to 20px. Here's comparison for the /fill-blur demo page.
Other demos look good too imo, but take a second look and if you approve it I'll update all the test cases.

Screenshot 2023-07-24 at 09 00 38 Screenshot 2023-07-24 at 09 00 47

@styfle styfle added the CI approved Approve running CI for fork label Jul 24, 2023
@@ -704,7 +704,7 @@ describe('Image Optimizer', () => {
const opts = { headers: { accept: 'image/webp' } }
const res = await fetchViaHTTP(appPort, '/_next/image', query, opts)
expect(res.status).toBe(200)
await expectWidth(res, 8)
await expectWidth(res, 320)
})
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@styfle We should probably rename this test or at least add some comment explaining the viewBox multiplier 8 * 40 = 320. Any suggestions?

Maybe should scale generated blur placeholder by 40?

Copy link
Member

@styfle styfle Jul 25, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, I think the name is correct because the 8 is referring about the input w.

@arturbien arturbien requested a review from styfle July 25, 2023 11:25
@arturbien
Copy link
Contributor Author

arturbien commented Jul 25, 2023

@styfle I've updated all relevant tests but some of the build-and-test / test turbopack integration (*) tests are failing. From the logs it looks like its unrelated to my changes but maybe I am missing something?

? 'xMidYMid slice'
: 'none'

return `%3Csvg xmlns='http://www.w3.org/2000/svg' ${viewBox}%3E%3Cfilter id='b' color-interpolation-filters='sRGB'%3E%3CfeGaussianBlur stdDeviation='${std}'/%3E%3CfeColorMatrix values='1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 100 -1' result='s'/%3E%3CfeFlood x='0' y='0' width='100%25' height='100%25'/%3E%3CfeComposite operator='out' in='s'/%3E%3CfeComposite in2='SourceGraphic'/%3E%3CfeGaussianBlur stdDeviation='${std}'/%3E%3C/filter%3E%3Cimage width='100%25' height='100%25' x='0' y='0' preserveAspectRatio='${preserveAspectRatio}' style='filter: url(%23b);' href='${blurDataURL}'/%3E%3C/svg%3E`
Copy link
Member

@styfle styfle Jul 25, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you change the order of the props so that the diff is easier to review?

Lets keep it: style, preserveAspectRatio, x, y, height, width, href

Copy link
Member

@styfle styfle left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work, thanks! 🎉

@arturbien
Copy link
Contributor Author

@styfle should I try to squash all of the commits first before we merge?

@styfle
Copy link
Member

styfle commented Jul 25, 2023

No need - we automatically squash on merge.

However, You can update the PR description with the updated details since that becomes the commit message.

@styfle styfle merged commit 7160af3 into vercel:canary Jul 26, 2023
49 checks passed
kwonoj pushed a commit that referenced this pull request Jul 26, 2023
Fixes #52548

This PR fixes the issue of the `<Image />` "blur" placeholder where the
placeholder image appears "washed out" and feathered around the edges.

The fix does NOT involve any API changes - only the underlying SVG
filter is updated.
The filter works for any image type (JPEG, GIF, WEBP) no matter if it
contains transparency or not.

## How it works

```html
  <filter id='b' color-interpolation-filters='sRGB'>
    <feMorphology in='SourceAlpha' operator='dilate' radius='15' result='dilate' />
    <feGaussianBlur in='dilate' stdDeviation='15' result='mask' />
    <feGaussianBlur in='SourceGraphic' stdDeviation='20' result='blur' />
    <feComponentTransfer in='blur' result='solid'>
      <feFuncA type='table' tableValues='1 1' />
    </feComponentTransfer>
    <feComposite in2='mask' in='solid' operator='in' result="comp" />
    <feMerge>
      <feMergeNode in="SourceGraphic" />
      <feMergeNode in="comp" />
    </feMerge>
  </filter>
  ```
Currently the underlying filter uses `feComponentTransfer ` to get rid of the feathered edges but only for JPEG images. This is because if alpha channel was to be mapped to 1 for images with transparency in it, all of the transparent pixels would turn black (which is obviously undesirable).

The way I fixed it is by creating a mask from the SourceAlpha (effectively the "shape" of the object in an image), slightly blurring it for a smoother look (feMorphology + feGaussianBlur), and then clipping the `feComponentTransfer` result to the shape of the mask (`<feComposite in2='mask' in='solid' operator='in' result="comp" />`). 

Then finally `feMerge` is used to stack original image and the clipping result from the previous step just to make sure that any left over artifacts from clipping are hidden.

## Result

Here's a comparison between the current implementation (left column), and the filter above (right column)

<img width="614" alt="Screenshot 2023-07-12 at 10 44 56" src="https://github.com/vercel/next.js/assets/28541613/afdc1f88-eb1a-4a21-a88a-06057a875e1b">

---------

Co-authored-by: Steven <steven@ceriously.com>
@arturbien
Copy link
Contributor Author

@styfle sorry I didn't manage to update the PR description - it was a night time for me.

Thank you @styfle and @ijjk for assisting me on this PR. ❤️

rfearing pushed a commit to rfearing/next.js that referenced this pull request Jul 26, 2023
Fixes vercel#52548

This PR fixes the issue of the `<Image />` "blur" placeholder where the
placeholder image appears "washed out" and feathered around the edges.

The fix does NOT involve any API changes - only the underlying SVG
filter is updated.
The filter works for any image type (JPEG, GIF, WEBP) no matter if it
contains transparency or not.

## How it works

```html
  <filter id='b' color-interpolation-filters='sRGB'>
    <feMorphology in='SourceAlpha' operator='dilate' radius='15' result='dilate' />
    <feGaussianBlur in='dilate' stdDeviation='15' result='mask' />
    <feGaussianBlur in='SourceGraphic' stdDeviation='20' result='blur' />
    <feComponentTransfer in='blur' result='solid'>
      <feFuncA type='table' tableValues='1 1' />
    </feComponentTransfer>
    <feComposite in2='mask' in='solid' operator='in' result="comp" />
    <feMerge>
      <feMergeNode in="SourceGraphic" />
      <feMergeNode in="comp" />
    </feMerge>
  </filter>
  ```
Currently the underlying filter uses `feComponentTransfer ` to get rid of the feathered edges but only for JPEG images. This is because if alpha channel was to be mapped to 1 for images with transparency in it, all of the transparent pixels would turn black (which is obviously undesirable).

The way I fixed it is by creating a mask from the SourceAlpha (effectively the "shape" of the object in an image), slightly blurring it for a smoother look (feMorphology + feGaussianBlur), and then clipping the `feComponentTransfer` result to the shape of the mask (`<feComposite in2='mask' in='solid' operator='in' result="comp" />`). 

Then finally `feMerge` is used to stack original image and the clipping result from the previous step just to make sure that any left over artifacts from clipping are hidden.

## Result

Here's a comparison between the current implementation (left column), and the filter above (right column)

<img width="614" alt="Screenshot 2023-07-12 at 10 44 56" src="https://github.com/vercel/next.js/assets/28541613/afdc1f88-eb1a-4a21-a88a-06057a875e1b">

---------

Co-authored-by: Steven <steven@ceriously.com>
@styfle
Copy link
Member

styfle commented Jul 26, 2023

@arturbien Since you have been spending a lot of time in this area, I wonder if you could solve this problem too:

If we could allow animated images embedded in the SVG, that would be ideal.

@arturbien
Copy link
Contributor Author

@styfle I'll look into it tomorrow 🤙

Strift pushed a commit to Strift/next.js that referenced this pull request Jul 27, 2023
Fixes vercel#52548

This PR fixes the issue of the `<Image />` "blur" placeholder where the
placeholder image appears "washed out" and feathered around the edges.

The fix does NOT involve any API changes - only the underlying SVG
filter is updated.
The filter works for any image type (JPEG, GIF, WEBP) no matter if it
contains transparency or not.

## How it works

```html
  <filter id='b' color-interpolation-filters='sRGB'>
    <feMorphology in='SourceAlpha' operator='dilate' radius='15' result='dilate' />
    <feGaussianBlur in='dilate' stdDeviation='15' result='mask' />
    <feGaussianBlur in='SourceGraphic' stdDeviation='20' result='blur' />
    <feComponentTransfer in='blur' result='solid'>
      <feFuncA type='table' tableValues='1 1' />
    </feComponentTransfer>
    <feComposite in2='mask' in='solid' operator='in' result="comp" />
    <feMerge>
      <feMergeNode in="SourceGraphic" />
      <feMergeNode in="comp" />
    </feMerge>
  </filter>
  ```
Currently the underlying filter uses `feComponentTransfer ` to get rid of the feathered edges but only for JPEG images. This is because if alpha channel was to be mapped to 1 for images with transparency in it, all of the transparent pixels would turn black (which is obviously undesirable).

The way I fixed it is by creating a mask from the SourceAlpha (effectively the "shape" of the object in an image), slightly blurring it for a smoother look (feMorphology + feGaussianBlur), and then clipping the `feComponentTransfer` result to the shape of the mask (`<feComposite in2='mask' in='solid' operator='in' result="comp" />`). 

Then finally `feMerge` is used to stack original image and the clipping result from the previous step just to make sure that any left over artifacts from clipping are hidden.

## Result

Here's a comparison between the current implementation (left column), and the filter above (right column)

<img width="614" alt="Screenshot 2023-07-12 at 10 44 56" src="https://github.com/vercel/next.js/assets/28541613/afdc1f88-eb1a-4a21-a88a-06057a875e1b">

---------

Co-authored-by: Steven <steven@ceriously.com>
? 'xMidYMid slice'
: 'none'
return `%3Csvg xmlns='http%3A//www.w3.org/2000/svg'%3E%3Cimage style='filter:blur(20px)' preserveAspectRatio='${preserveAspectRatio}' x='0' y='0' height='100%25' width='100%25' href='${blurDataURL}'/%3E%3C/svg%3E`
const std = 20

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Magic number why 20? Also we should consider renaming std, what is it short for?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah "std" was supposed to stand for "standard deviation" but I agree that it sounds bad now that I think about it 😂

It should be renamed to "blur".

And the blur value is set to 20 because it looks good.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Aug 26, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
CI approved Approve running CI for fork locked type: next
Projects
None yet
Development

Successfully merging this pull request may close these issues.

<Image /> placeholder image appears "washed out" due to feathered edges
4 participants