From 877c02ccfc2e13d5aabaf4f6b27ba2be55209e6b Mon Sep 17 00:00:00 2001
From: Bad Manners <me@badmanners.xyz>
Date: Wed, 3 Apr 2024 20:06:21 -0300
Subject: [PATCH] Add Pagefind searching

---
 astro.config.mjs                              |   2 +
 package-lock.json                             | 892 ++++++++++++++----
 package.json                                  |  20 +-
 src/components/Navigation.astro               |   6 +
 src/layouts/GalleryLayout.astro               |  14 +-
 src/layouts/GameLayout.astro                  |  18 +-
 src/layouts/StoryLayout.astro                 |  19 +-
 src/pages/games.astro                         |   2 +-
 src/pages/index.astro                         |   2 +-
 src/pages/search.astro                        |  11 +
 src/pages/stories/[page].astro                |   2 +-
 .../stories/the-lost-of-the-marshes.astro     |  11 +-
 src/pages/tags.astro                          |   4 +-
 src/styles/base.css                           |  32 +-
 14 files changed, 828 insertions(+), 207 deletions(-)
 create mode 100644 src/pages/search.astro

diff --git a/astro.config.mjs b/astro.config.mjs
index f594e4e..2abf16d 100644
--- a/astro.config.mjs
+++ b/astro.config.mjs
@@ -1,6 +1,7 @@
 import { defineConfig } from "astro/config";
 import tailwindIntegration from "@astrojs/tailwind";
 import markdownIntegration from "@astropub/md";
+import pagefindIntegration from "astro-pagefind";
 
 // https://astro.build/config
 export default defineConfig({
@@ -10,6 +11,7 @@ export default defineConfig({
       applyBaseStyles: false,
     }),
     markdownIntegration(),
+    pagefindIntegration(),
   ],
   markdown: {
     smartypants: false,
diff --git a/package-lock.json b/package-lock.json
index 5a79535..9ba452b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,34 +1,59 @@
 {
   "name": "gallery-badmanners-xyz",
-  "version": "1.2.2",
+  "version": "1.3.0",
   "lockfileVersion": 3,
   "requires": true,
   "packages": {
     "": {
       "name": "gallery-badmanners-xyz",
-      "version": "1.2.2",
+      "version": "1.3.0",
       "dependencies": {
-        "@astrojs/check": "^0.5.9",
+        "@astrojs/check": "^0.5.10",
         "@astrojs/rss": "^4.0.5",
         "@astrojs/tailwind": "^5.1.0",
         "@astropub/md": "^0.4.0",
-        "@tailwindcss/typography": "^0.5.10",
+        "@tailwindcss/typography": "^0.5.12",
         "@types/sanitize-html": "^2.11.0",
-        "astro": "^4.5.4",
+        "astro": "^4.5.16",
+        "astro-pagefind": "^1.5.0",
         "github-slugger": "^2.0.0",
         "marked": "^12.0.1",
+        "pagefind": "^1.1.0",
         "sanitize-html": "^2.13.0",
-        "tailwindcss": "^3.4.1",
+        "tailwindcss": "^3.4.3",
         "tiny-decode": "^0.1.3",
-        "typescript": "^5.4.2"
+        "typescript": "^5.4.4"
       },
       "devDependencies": {
         "commander": "^12.0.0",
         "fetch-retry": "^6.0.0",
         "prettier": "^3.2.5",
         "prettier-plugin-astro": "^0.13.0",
-        "prettier-plugin-tailwindcss": "^0.5.12",
-        "tsx": "^4.7.1"
+        "prettier-plugin-tailwindcss": "^0.5.13",
+        "tsx": "^4.7.2"
+      }
+    },
+    "../astro-pagefind/packages/astro-pagefind": {
+      "version": "0.0.0-development",
+      "extraneous": true,
+      "license": "MIT",
+      "dependencies": {
+        "@pagefind/default-ui": "^1.0.3",
+        "pagefind": "^1.0.3",
+        "sirv": "^2.0.3"
+      },
+      "devDependencies": {
+        "@astrojs/check": "0.5.9",
+        "@astrojs/markdown-remark": "4.3.0",
+        "@semantic-release/changelog": "6.0.3",
+        "@semantic-release/git": "10.0.1",
+        "@types/semantic-release": "20.0.6",
+        "astro": "4.5.5",
+        "semantic-release": "23.0.4",
+        "typescript": "5.4.2"
+      },
+      "peerDependencies": {
+        "astro": "^2.0.4 || ^3.0.0 || ^4.0.0"
       }
     },
     "node_modules/@alloc/quick-lru": {
@@ -55,11 +80,11 @@
       }
     },
     "node_modules/@astrojs/check": {
-      "version": "0.5.9",
-      "resolved": "https://registry.npmjs.org/@astrojs/check/-/check-0.5.9.tgz",
-      "integrity": "sha512-+QsQMtYq4oso+gmilJC9HLmdi0glZ+04V/VyyTTPry7n21jqjX9SfgDpLGxMk5cwPC/vwZMkn6ORGPnkZS/L5w==",
+      "version": "0.5.10",
+      "resolved": "https://registry.npmjs.org/@astrojs/check/-/check-0.5.10.tgz",
+      "integrity": "sha512-vliHXM9cu/viGeKiksUM4mXfO816ohWtawTl2ADPgTsd4nUMjFiyAl7xFZhF34yy4hq4qf7jvK1F2PlR3b5I5w==",
       "dependencies": {
-        "@astrojs/language-server": "^2.8.1",
+        "@astrojs/language-server": "^2.8.4",
         "chokidar": "^3.5.3",
         "fast-glob": "^3.3.1",
         "kleur": "^4.1.5",
@@ -73,34 +98,34 @@
       }
     },
     "node_modules/@astrojs/compiler": {
-      "version": "2.7.0",
-      "resolved": "https://registry.npmjs.org/@astrojs/compiler/-/compiler-2.7.0.tgz",
-      "integrity": "sha512-XpC8MAaWjD1ff6/IfkRq/5k1EFj6zhCNqXRd5J43SVJEBj/Bsmizkm8N0xOYscGcDFQkRgEw6/eKnI5x/1l6aA=="
+      "version": "2.7.1",
+      "resolved": "https://registry.npmjs.org/@astrojs/compiler/-/compiler-2.7.1.tgz",
+      "integrity": "sha512-/POejAYuj8WEw7ZI0J8JBvevjfp9jQ9Wmu/Bg52RiNwGXkMV7JnYpsenVfHvvf1G7R5sXHGKlTcxlQWhoUTiGQ=="
     },
     "node_modules/@astrojs/internal-helpers": {
-      "version": "0.3.0",
-      "resolved": "https://registry.npmjs.org/@astrojs/internal-helpers/-/internal-helpers-0.3.0.tgz",
-      "integrity": "sha512-tGmHvrhpzuz0JBHaJX8GywN9g4rldVNHtkoVDC3m/DdzBO70jGoVuc0uuNVglRYnsdwkbG0K02Iw3nOOR3/Y4g=="
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/@astrojs/internal-helpers/-/internal-helpers-0.4.0.tgz",
+      "integrity": "sha512-6B13lz5n6BrbTqCTwhXjJXuR1sqiX/H6rTxzlXx+lN1NnV4jgnq/KJldCQaUWJzPL5SiWahQyinxAbxQtwgPHA=="
     },
     "node_modules/@astrojs/language-server": {
-      "version": "2.8.2",
-      "resolved": "https://registry.npmjs.org/@astrojs/language-server/-/language-server-2.8.2.tgz",
-      "integrity": "sha512-8BfOqx4kYSZqLxpXezryoblg1z4ufgWAh7Y9iT/3g8sUzG1jE1MVdwxXixRbsOu9X4bgLDLMwbOgXp63Fbd/zA==",
+      "version": "2.8.4",
+      "resolved": "https://registry.npmjs.org/@astrojs/language-server/-/language-server-2.8.4.tgz",
+      "integrity": "sha512-sJH5vGTBkhgA8+hdhzX78UUp4cFz4Mt7xkEkevD188OS5bDMkaue6hK+dtXWM47mnrXFveXA2u38K7S+5+IRjA==",
       "dependencies": {
         "@astrojs/compiler": "^2.7.0",
         "@jridgewell/sourcemap-codec": "^1.4.15",
-        "@volar/kit": "~2.1.2",
-        "@volar/language-core": "~2.1.2",
-        "@volar/language-server": "~2.1.2",
-        "@volar/language-service": "~2.1.2",
-        "@volar/typescript": "~2.1.2",
+        "@volar/kit": "~2.1.5",
+        "@volar/language-core": "~2.1.5",
+        "@volar/language-server": "~2.1.5",
+        "@volar/language-service": "~2.1.5",
+        "@volar/typescript": "~2.1.5",
         "fast-glob": "^3.2.12",
-        "volar-service-css": "0.0.33",
-        "volar-service-emmet": "0.0.33",
-        "volar-service-html": "0.0.33",
-        "volar-service-prettier": "0.0.33",
-        "volar-service-typescript": "0.0.33",
-        "volar-service-typescript-twoslash-queries": "0.0.33",
+        "volar-service-css": "0.0.34",
+        "volar-service-emmet": "0.0.34",
+        "volar-service-html": "0.0.34",
+        "volar-service-prettier": "0.0.34",
+        "volar-service-typescript": "0.0.34",
+        "volar-service-typescript-twoslash-queries": "0.0.34",
         "vscode-html-languageservice": "^5.1.2",
         "vscode-uri": "^3.0.8"
       },
@@ -124,6 +149,7 @@
       "version": "4.3.0",
       "resolved": "https://registry.npmjs.org/@astrojs/markdown-remark/-/markdown-remark-4.3.0.tgz",
       "integrity": "sha512-iZOgYj/yNDvBRfKqkGuAvjeONhjQPq8Uk3HjyIgcTK5valq03NiUgSc5Ovq00yUVBeYJ/5EDx23c8xqtkkBlPw==",
+      "peer": true,
       "dependencies": {
         "@astrojs/prism": "^3.0.0",
         "github-slugger": "^2.0.0",
@@ -219,12 +245,12 @@
       }
     },
     "node_modules/@babel/code-frame": {
-      "version": "7.23.5",
-      "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz",
-      "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==",
+      "version": "7.24.2",
+      "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz",
+      "integrity": "sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==",
       "dependencies": {
-        "@babel/highlight": "^7.23.4",
-        "chalk": "^2.4.2"
+        "@babel/highlight": "^7.24.2",
+        "picocolors": "^1.0.0"
       },
       "engines": {
         "node": ">=6.9.0"
@@ -239,19 +265,19 @@
       }
     },
     "node_modules/@babel/core": {
-      "version": "7.24.0",
-      "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.0.tgz",
-      "integrity": "sha512-fQfkg0Gjkza3nf0c7/w6Xf34BW4YvzNfACRLmmb7XRLa6XHdR+K9AlJlxneFfWYf6uhOzuzZVTjF/8KfndZANw==",
+      "version": "7.24.4",
+      "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.4.tgz",
+      "integrity": "sha512-MBVlMXP+kkl5394RBLSxxk/iLTeVGuXTV3cIDXavPpMMqnSnt6apKgan/U8O3USWZCWZT/TbgfEpKa4uMgN4Dg==",
       "dependencies": {
         "@ampproject/remapping": "^2.2.0",
-        "@babel/code-frame": "^7.23.5",
-        "@babel/generator": "^7.23.6",
+        "@babel/code-frame": "^7.24.2",
+        "@babel/generator": "^7.24.4",
         "@babel/helper-compilation-targets": "^7.23.6",
         "@babel/helper-module-transforms": "^7.23.3",
-        "@babel/helpers": "^7.24.0",
-        "@babel/parser": "^7.24.0",
+        "@babel/helpers": "^7.24.4",
+        "@babel/parser": "^7.24.4",
         "@babel/template": "^7.24.0",
-        "@babel/traverse": "^7.24.0",
+        "@babel/traverse": "^7.24.1",
         "@babel/types": "^7.24.0",
         "convert-source-map": "^2.0.0",
         "debug": "^4.1.0",
@@ -276,13 +302,13 @@
       }
     },
     "node_modules/@babel/generator": {
-      "version": "7.23.6",
-      "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.6.tgz",
-      "integrity": "sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==",
+      "version": "7.24.4",
+      "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.4.tgz",
+      "integrity": "sha512-Xd6+v6SnjWVx/nus+y0l1sxMOTOMBkyL4+BIdbALyatQnAe/SRVjANeDPSCYaX+i1iJmuGSKf3Z+E+V/va1Hvw==",
       "dependencies": {
-        "@babel/types": "^7.23.6",
-        "@jridgewell/gen-mapping": "^0.3.2",
-        "@jridgewell/trace-mapping": "^0.3.17",
+        "@babel/types": "^7.24.0",
+        "@jridgewell/gen-mapping": "^0.3.5",
+        "@jridgewell/trace-mapping": "^0.3.25",
         "jsesc": "^2.5.1"
       },
       "engines": {
@@ -438,12 +464,12 @@
       }
     },
     "node_modules/@babel/helpers": {
-      "version": "7.24.0",
-      "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.0.tgz",
-      "integrity": "sha512-ulDZdc0Aj5uLc5nETsa7EPx2L7rM0YJM8r7ck7U73AXi7qOV44IHHRAYZHY6iU1rr3C5N4NtTmMRUJP6kwCWeA==",
+      "version": "7.24.4",
+      "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.4.tgz",
+      "integrity": "sha512-FewdlZbSiwaVGlgT1DPANDuCHaDMiOo+D/IDYRFYjHOuv66xMSJ7fQwwODwRNAPkADIO/z1EoF/l2BCWlWABDw==",
       "dependencies": {
         "@babel/template": "^7.24.0",
-        "@babel/traverse": "^7.24.0",
+        "@babel/traverse": "^7.24.1",
         "@babel/types": "^7.24.0"
       },
       "engines": {
@@ -451,22 +477,23 @@
       }
     },
     "node_modules/@babel/highlight": {
-      "version": "7.23.4",
-      "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz",
-      "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==",
+      "version": "7.24.2",
+      "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.2.tgz",
+      "integrity": "sha512-Yac1ao4flkTxTteCDZLEvdxg2fZfz1v8M4QpaGypq/WPDqg3ijHYbDfs+LG5hvzSoqaSZ9/Z9lKSP3CjZjv+pA==",
       "dependencies": {
         "@babel/helper-validator-identifier": "^7.22.20",
         "chalk": "^2.4.2",
-        "js-tokens": "^4.0.0"
+        "js-tokens": "^4.0.0",
+        "picocolors": "^1.0.0"
       },
       "engines": {
         "node": ">=6.9.0"
       }
     },
     "node_modules/@babel/parser": {
-      "version": "7.24.0",
-      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.0.tgz",
-      "integrity": "sha512-QuP/FxEAzMSjXygs8v4N9dvdXzEHN4W1oF3PxuWAtPo08UdM17u89RDMgjLn/mlc56iM0HlLmVkO/wgR+rDgHg==",
+      "version": "7.24.4",
+      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.4.tgz",
+      "integrity": "sha512-zTvEBcghmeBma9QIGunWevvBAp4/Qu9Bdq+2k0Ot4fVMD6v3dsC9WOcRSKk7tRRyBM/53yKMJko9xOatGQAwSg==",
       "bin": {
         "parser": "bin/babel-parser.js"
       },
@@ -520,17 +547,17 @@
       }
     },
     "node_modules/@babel/traverse": {
-      "version": "7.24.0",
-      "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.0.tgz",
-      "integrity": "sha512-HfuJlI8qq3dEDmNU5ChzzpZRWq+oxCZQyMzIMEqLho+AQnhMnKQUzH6ydo3RBl/YjPCuk68Y6s0Gx0AeyULiWw==",
+      "version": "7.24.1",
+      "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.1.tgz",
+      "integrity": "sha512-xuU6o9m68KeqZbQuDt2TcKSxUw/mrsvavlEqQ1leZ/B+C9tk6E4sRWy97WaXgvq5E+nU3cXMxv3WKOCanVMCmQ==",
       "dependencies": {
-        "@babel/code-frame": "^7.23.5",
-        "@babel/generator": "^7.23.6",
+        "@babel/code-frame": "^7.24.1",
+        "@babel/generator": "^7.24.1",
         "@babel/helper-environment-visitor": "^7.22.20",
         "@babel/helper-function-name": "^7.23.0",
         "@babel/helper-hoist-variables": "^7.22.5",
         "@babel/helper-split-export-declaration": "^7.22.6",
-        "@babel/parser": "^7.24.0",
+        "@babel/parser": "^7.24.1",
         "@babel/types": "^7.24.0",
         "debug": "^4.3.1",
         "globals": "^11.1.0"
@@ -1054,6 +1081,71 @@
         "node": ">= 8"
       }
     },
+    "node_modules/@pagefind/darwin-arm64": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@pagefind/darwin-arm64/-/darwin-arm64-1.1.0.tgz",
+      "integrity": "sha512-SLsXNLtSilGZjvqis8sX42fBWsWAVkcDh1oerxwqbac84HbiwxpxOC2jm8hRwcR0Z55HPZPWO77XeRix/8GwTg==",
+      "cpu": [
+        "arm64"
+      ],
+      "optional": true,
+      "os": [
+        "darwin"
+      ]
+    },
+    "node_modules/@pagefind/darwin-x64": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@pagefind/darwin-x64/-/darwin-x64-1.1.0.tgz",
+      "integrity": "sha512-QjQSE/L5oS1C8N8GdljGaWtjCBMgMtfrPAoiCmINTu9Y9dp0ggAyXvF8K7Qg3VyIMYJ6v8vg2PN7Z3b+AaAqUA==",
+      "cpu": [
+        "x64"
+      ],
+      "optional": true,
+      "os": [
+        "darwin"
+      ]
+    },
+    "node_modules/@pagefind/default-ui": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@pagefind/default-ui/-/default-ui-1.1.0.tgz",
+      "integrity": "sha512-+XiAJAK++C64nQcD7s3Prdmd5S92lT05fwjOxm0L1jj80jbL+tmvcqkkFnPpoqhnicIPgcAX/Y5W0HRZnBt35w=="
+    },
+    "node_modules/@pagefind/linux-arm64": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@pagefind/linux-arm64/-/linux-arm64-1.1.0.tgz",
+      "integrity": "sha512-8zjYCa2BtNEL7KnXtysPtBELCyv5DSQ4yHeK/nsEq6w4ToAMTBl0K06khqxdSGgjMSwwrxvLzq3so0LC5Q14dA==",
+      "cpu": [
+        "arm64"
+      ],
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@pagefind/linux-x64": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@pagefind/linux-x64/-/linux-x64-1.1.0.tgz",
+      "integrity": "sha512-4lsg6VB7A6PWTwaP8oSmXV4O9H0IHX7AlwTDcfyT+YJo/sPXOVjqycD5cdBgqNLfUk8B9bkWcTDCRmJbHrKeCw==",
+      "cpu": [
+        "x64"
+      ],
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@pagefind/windows-x64": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@pagefind/windows-x64/-/windows-x64-1.1.0.tgz",
+      "integrity": "sha512-OboCM76BcMKT9IoSfZuFhiqMRgTde8x4qDDvKulFmycgiJrlL5WnIqBHJLQxZq+o2KyZpoHF97iwsGAm8c32sQ==",
+      "cpu": [
+        "x64"
+      ],
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
     "node_modules/@pkgjs/parseargs": {
       "version": "0.11.0",
       "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
@@ -1063,6 +1155,11 @@
         "node": ">=14"
       }
     },
+    "node_modules/@polka/url": {
+      "version": "1.0.0-next.25",
+      "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.25.tgz",
+      "integrity": "sha512-j7P6Rgr3mmtdkeDGTe0E/aYyWEWVtc5yFXtHCRHs28/jptDEWfaVOc5T7cblqy1XKPPfCxJc/8DwQ5YgLOZOVQ=="
+    },
     "node_modules/@rollup/rollup-android-arm-eabi": {
       "version": "4.13.0",
       "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.13.0.tgz",
@@ -1225,9 +1322,9 @@
       "integrity": "sha512-gTYLUIuD1UbZp/11qozD3fWpUTuMqPSf3svDMMrL0UmlGU7D9dPw/V1FonwAorCUJBltaaESxq90jrSjQyGixg=="
     },
     "node_modules/@tailwindcss/typography": {
-      "version": "0.5.10",
-      "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.10.tgz",
-      "integrity": "sha512-Pe8BuPJQJd3FfRnm6H0ulKIGoMEQS+Vq01R6M5aCrFB/ccR/shT+0kXLjouGC1gFLm9hopTFN+DMP0pfwRWzPw==",
+      "version": "0.5.12",
+      "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.12.tgz",
+      "integrity": "sha512-CNwpBpconcP7ppxmuq3qvaCxiRWnbhANpY/ruH4L5qs2GCiVDJXde/pjj2HWPV1+Q4G9+V/etrwUYopdcjAlyg==",
       "dependencies": {
         "lodash.castarray": "^4.4.0",
         "lodash.isplainobject": "^4.0.6",
@@ -1391,12 +1488,12 @@
       "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ=="
     },
     "node_modules/@volar/kit": {
-      "version": "2.1.2",
-      "resolved": "https://registry.npmjs.org/@volar/kit/-/kit-2.1.2.tgz",
-      "integrity": "sha512-u20R1lCWCgFYBCHC+FR/e9J+P61vUNQpyWt4keAY+zpVHEHsSXVA2xWMJV1l1Iq5Dd0jBUSqrb1zsEya455AzA==",
+      "version": "2.1.6",
+      "resolved": "https://registry.npmjs.org/@volar/kit/-/kit-2.1.6.tgz",
+      "integrity": "sha512-dSuXChDGM0nSG/0fxqlNfadjpAeeo1P1SJPBQ+pDf8H1XrqeJq5gIhxRTEbiS+dyNIG69ATq1CArkbCif+oxJw==",
       "dependencies": {
-        "@volar/language-service": "2.1.2",
-        "@volar/typescript": "2.1.2",
+        "@volar/language-service": "2.1.6",
+        "@volar/typescript": "2.1.6",
         "typesafe-path": "^0.2.2",
         "vscode-languageserver-textdocument": "^1.0.11",
         "vscode-uri": "^3.0.8"
@@ -1406,22 +1503,22 @@
       }
     },
     "node_modules/@volar/language-core": {
-      "version": "2.1.2",
-      "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.1.2.tgz",
-      "integrity": "sha512-5qsDp0Gf6fE09UWCeK7bkVn6NxMwC9OqFWQkMMkeej8h8XjyABPdRygC2RCrqDrfVdGijqlMQeXs6yRS+vfZYA==",
+      "version": "2.1.6",
+      "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.1.6.tgz",
+      "integrity": "sha512-pAlMCGX/HatBSiDFMdMyqUshkbwWbLxpN/RL7HCQDOo2gYBE+uS+nanosLc1qR6pTQ/U8q00xt8bdrrAFPSC0A==",
       "dependencies": {
-        "@volar/source-map": "2.1.2"
+        "@volar/source-map": "2.1.6"
       }
     },
     "node_modules/@volar/language-server": {
-      "version": "2.1.2",
-      "resolved": "https://registry.npmjs.org/@volar/language-server/-/language-server-2.1.2.tgz",
-      "integrity": "sha512-5NR5Ztg+OxvDI4oRrjS0/4ZVPumWwhVq5acuK2BJbakG1kJXViYI9NOWiWITMjnliPvf12TEcSrVDBmIq54DOg==",
+      "version": "2.1.6",
+      "resolved": "https://registry.npmjs.org/@volar/language-server/-/language-server-2.1.6.tgz",
+      "integrity": "sha512-0w+FV8ro37hVb3qE4ONo3VbS5kEQXv4H/D2xCePyY5dRw6XnbJAPFNKvoxI9mxHTPonvIG1si5rN9MSGSKtgZQ==",
       "dependencies": {
-        "@volar/language-core": "2.1.2",
-        "@volar/language-service": "2.1.2",
-        "@volar/snapshot-document": "2.1.2",
-        "@volar/typescript": "2.1.2",
+        "@volar/language-core": "2.1.6",
+        "@volar/language-service": "2.1.6",
+        "@volar/snapshot-document": "2.1.6",
+        "@volar/typescript": "2.1.6",
         "@vscode/l10n": "^0.0.16",
         "path-browserify": "^1.0.1",
         "request-light": "^0.7.0",
@@ -1432,39 +1529,39 @@
       }
     },
     "node_modules/@volar/language-service": {
-      "version": "2.1.2",
-      "resolved": "https://registry.npmjs.org/@volar/language-service/-/language-service-2.1.2.tgz",
-      "integrity": "sha512-CmVbbKdqzVq+0FT67hfELdHpboqXhKXh6EjypypuFX5ptIRftHZdkaq3/lCCa46EHxS5tvE44jn+s7faN4iRDA==",
+      "version": "2.1.6",
+      "resolved": "https://registry.npmjs.org/@volar/language-service/-/language-service-2.1.6.tgz",
+      "integrity": "sha512-1OpbbPQ6wUIumwMP5r45y8utVEmvq1n6BC8JHqGKsuFr9RGFIldDBlvA/xuO3MDKhjmmPGPHKb54kg1/YN78ow==",
       "dependencies": {
-        "@volar/language-core": "2.1.2",
+        "@volar/language-core": "2.1.6",
         "vscode-languageserver-protocol": "^3.17.5",
         "vscode-languageserver-textdocument": "^1.0.11",
         "vscode-uri": "^3.0.8"
       }
     },
     "node_modules/@volar/snapshot-document": {
-      "version": "2.1.2",
-      "resolved": "https://registry.npmjs.org/@volar/snapshot-document/-/snapshot-document-2.1.2.tgz",
-      "integrity": "sha512-ZpJIBZrdm/Gx4jC/zn8H+O6H5vZZwY7B5CMTxl9y8HvcqlePOyDi+VkX8pjQz1VFG9Z5Z+Bau/RL6exqkoVDDA==",
+      "version": "2.1.6",
+      "resolved": "https://registry.npmjs.org/@volar/snapshot-document/-/snapshot-document-2.1.6.tgz",
+      "integrity": "sha512-YNYk1sCOrGg7VHbZM+1It97q0GWhFxdqIwnxSNFoL0X1LuSRXoCT2DRb/aa1J6aBpPMbKqSFUWHGQEAFUnc4Zw==",
       "dependencies": {
         "vscode-languageserver-protocol": "^3.17.5",
         "vscode-languageserver-textdocument": "^1.0.11"
       }
     },
     "node_modules/@volar/source-map": {
-      "version": "2.1.2",
-      "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.1.2.tgz",
-      "integrity": "sha512-yFJqsuLm1OaWrsz9E3yd3bJcYIlHqdZ8MbmIoZLrAzMYQDcoF26/INIhgziEXSdyHc8xd7rd/tJdSnUyh0gH4Q==",
+      "version": "2.1.6",
+      "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.1.6.tgz",
+      "integrity": "sha512-TeyH8pHHonRCHYI91J7fWUoxi0zWV8whZTVRlsWHSYfjm58Blalkf9LrZ+pj6OiverPTmrHRkBsG17ScQyWECw==",
       "dependencies": {
         "muggle-string": "^0.4.0"
       }
     },
     "node_modules/@volar/typescript": {
-      "version": "2.1.2",
-      "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.1.2.tgz",
-      "integrity": "sha512-lhTancZqamvaLvoz0u/uth8dpudENNt2LFZOWCw9JZiX14xRFhdhfzmphiCRb7am9E6qAJSbdS/gMt1utXAoHQ==",
+      "version": "2.1.6",
+      "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.1.6.tgz",
+      "integrity": "sha512-JgPGhORHqXuyC3r6skPmPHIZj4LoMmGlYErFTuPNBq9Nhc9VTv7ctHY7A3jMN3ngKEfRrfnUcwXHztvdSQqNfw==",
       "dependencies": {
-        "@volar/language-core": "2.1.2",
+        "@volar/language-core": "2.1.6",
         "path-browserify": "^1.0.1"
       }
     },
@@ -1623,15 +1720,15 @@
       }
     },
     "node_modules/astro": {
-      "version": "4.5.4",
-      "resolved": "https://registry.npmjs.org/astro/-/astro-4.5.4.tgz",
-      "integrity": "sha512-qwMyBn36wXvaCzdCmdbyCxLQGG5hTa3saNdHdqS20Yn1b7Z7oqqcWOb2UjFuq6O8dFqZxU8Hq0/pGFscvsYtJA==",
+      "version": "4.5.16",
+      "resolved": "https://registry.npmjs.org/astro/-/astro-4.5.16.tgz",
+      "integrity": "sha512-1nOVsMq2OJiXnG6gO0Y77vTAboGN9nLQSy/8SGazq4h6x+alzbsMbQbArBgvaLzOSUXD0m91XLs3D8bOSuavrQ==",
       "dependencies": {
-        "@astrojs/compiler": "^2.7.0",
-        "@astrojs/internal-helpers": "0.3.0",
-        "@astrojs/markdown-remark": "4.3.0",
+        "@astrojs/compiler": "^2.7.1",
+        "@astrojs/internal-helpers": "0.4.0",
+        "@astrojs/markdown-remark": "5.0.0",
         "@astrojs/telemetry": "3.0.4",
-        "@babel/core": "^7.23.3",
+        "@babel/core": "^7.24.3",
         "@babel/generator": "^7.23.3",
         "@babel/parser": "^7.23.3",
         "@babel/plugin-transform-react-jsx": "^7.22.5",
@@ -1701,6 +1798,44 @@
         "sharp": "^0.32.6"
       }
     },
+    "node_modules/astro-pagefind": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/astro-pagefind/-/astro-pagefind-1.5.0.tgz",
+      "integrity": "sha512-CN7Afe9qW640U1qliCQMXN259Dl6VPUnl8FneLfKE7STV4HLiif4PbNZejylC6yXYyP6uyNVDHNOfJfBBW5h6A==",
+      "dependencies": {
+        "@pagefind/default-ui": "^1.0.3",
+        "pagefind": "^1.0.3",
+        "sirv": "^2.0.3"
+      },
+      "peerDependencies": {
+        "astro": "^2.0.4 || ^3.0.0 || ^4.0.0"
+      }
+    },
+    "node_modules/astro/node_modules/@astrojs/markdown-remark": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/@astrojs/markdown-remark/-/markdown-remark-5.0.0.tgz",
+      "integrity": "sha512-QBXbxXZamVRoqCNN2gjDXa7qYPUkJZq7KYFfg3DX7rze3QL6xiz4N+Wg202dNPRaIkQa16BV6D8+EHibQFubRg==",
+      "dependencies": {
+        "@astrojs/prism": "^3.0.0",
+        "github-slugger": "^2.0.0",
+        "hast-util-from-html": "^2.0.0",
+        "hast-util-to-text": "^4.0.0",
+        "import-meta-resolve": "^4.0.0",
+        "mdast-util-definitions": "^6.0.0",
+        "rehype-raw": "^7.0.0",
+        "rehype-stringify": "^10.0.0",
+        "remark-gfm": "^4.0.0",
+        "remark-parse": "^11.0.0",
+        "remark-rehype": "^11.0.0",
+        "remark-smartypants": "^2.0.0",
+        "shiki": "^1.1.2",
+        "unified": "^11.0.4",
+        "unist-util-remove-position": "^5.0.0",
+        "unist-util-visit": "^5.0.0",
+        "unist-util-visit-parents": "^6.0.0",
+        "vfile": "^6.0.1"
+      }
+    },
     "node_modules/autoprefixer": {
       "version": "10.4.18",
       "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.18.tgz",
@@ -2556,12 +2691,6 @@
       "version": "2.4.7",
       "resolved": "https://registry.npmjs.org/emmet/-/emmet-2.4.7.tgz",
       "integrity": "sha512-O5O5QNqtdlnQM2bmKHtJgyChcrFMgQuulI+WdiOw2NArzprUqqxUW6bgYtKvzKgrsYpuLWalOkdhNP+1jluhCA==",
-      "workspaces": [
-        "./packages/scanner",
-        "./packages/abbreviation",
-        "./packages/css-abbreviation",
-        "./"
-      ],
       "dependencies": {
         "@emmetio/abbreviation": "^2.3.3",
         "@emmetio/css-abbreviation": "^2.1.8"
@@ -4578,6 +4707,14 @@
       "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==",
       "optional": true
     },
+    "node_modules/mrmime": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.0.tgz",
+      "integrity": "sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==",
+      "engines": {
+        "node": ">=10"
+      }
+    },
     "node_modules/ms": {
       "version": "2.1.2",
       "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
@@ -4872,6 +5009,21 @@
         "node": ">=6"
       }
     },
+    "node_modules/pagefind": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/pagefind/-/pagefind-1.1.0.tgz",
+      "integrity": "sha512-1nmj0/vfYcMxNEQj0YDRp6bTVv9hI7HLdPhK/vBBYlrnwjATndQvHyicj5Y7pUHrpCFZpFnLVQXIF829tpFmaw==",
+      "bin": {
+        "pagefind": "lib/runner/bin.cjs"
+      },
+      "optionalDependencies": {
+        "@pagefind/darwin-arm64": "1.1.0",
+        "@pagefind/darwin-x64": "1.1.0",
+        "@pagefind/linux-arm64": "1.1.0",
+        "@pagefind/linux-x64": "1.1.0",
+        "@pagefind/windows-x64": "1.1.0"
+      }
+    },
     "node_modules/parse-latin": {
       "version": "5.0.1",
       "resolved": "https://registry.npmjs.org/parse-latin/-/parse-latin-5.0.1.tgz",
@@ -5048,9 +5200,9 @@
       }
     },
     "node_modules/postcss": {
-      "version": "8.4.35",
-      "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz",
-      "integrity": "sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==",
+      "version": "8.4.38",
+      "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz",
+      "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==",
       "funding": [
         {
           "type": "opencollective",
@@ -5068,7 +5220,7 @@
       "dependencies": {
         "nanoid": "^3.3.7",
         "picocolors": "^1.0.0",
-        "source-map-js": "^1.0.2"
+        "source-map-js": "^1.2.0"
       },
       "engines": {
         "node": "^10 || ^12 || >=14"
@@ -5328,9 +5480,9 @@
       "devOptional": true
     },
     "node_modules/prettier-plugin-tailwindcss": {
-      "version": "0.5.12",
-      "resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.5.12.tgz",
-      "integrity": "sha512-o74kiDBVE73oHW+pdkFSluHBL3cYEvru5YgEqNkBMFF7Cjv+w1vI565lTlfoJT4VLWDe0FMtZ7FkE/7a4pMXSQ==",
+      "version": "0.5.13",
+      "resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.5.13.tgz",
+      "integrity": "sha512-2tPWHCFNC+WRjAC4SIWQNSOdcL1NNkydXim8w7TDqlZi+/ulZYz2OouAI6qMtkggnPt7lGamboj6LcTMwcCvoQ==",
       "dev": true,
       "engines": {
         "node": ">=14.21.3"
@@ -5340,6 +5492,7 @@
         "@prettier/plugin-pug": "*",
         "@shopify/prettier-plugin-liquid": "*",
         "@trivago/prettier-plugin-sort-imports": "*",
+        "@zackad/prettier-plugin-twig-melody": "*",
         "prettier": "^3.0",
         "prettier-plugin-astro": "*",
         "prettier-plugin-css-order": "*",
@@ -5365,6 +5518,9 @@
         "@trivago/prettier-plugin-sort-imports": {
           "optional": true
         },
+        "@zackad/prettier-plugin-twig-melody": {
+          "optional": true
+        },
         "prettier-plugin-astro": {
           "optional": true
         },
@@ -5394,9 +5550,6 @@
         },
         "prettier-plugin-svelte": {
           "optional": true
-        },
-        "prettier-plugin-twig-melody": {
-          "optional": true
         }
       }
     },
@@ -6366,15 +6519,28 @@
         "is-arrayish": "^0.3.1"
       }
     },
+    "node_modules/sirv": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.4.tgz",
+      "integrity": "sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==",
+      "dependencies": {
+        "@polka/url": "^1.0.0-next.24",
+        "mrmime": "^2.0.0",
+        "totalist": "^3.0.0"
+      },
+      "engines": {
+        "node": ">= 10"
+      }
+    },
     "node_modules/sisteransi": {
       "version": "1.0.5",
       "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz",
       "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="
     },
     "node_modules/source-map-js": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
-      "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz",
+      "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==",
       "engines": {
         "node": ">=0.10.0"
       }
@@ -6631,9 +6797,9 @@
       }
     },
     "node_modules/tailwindcss": {
-      "version": "3.4.1",
-      "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.1.tgz",
-      "integrity": "sha512-qAYmXRfk3ENzuPBakNK0SRrUDipP8NQnEY6772uDhflcQz5EhRdD7JNZxyrFHVQNCwULPBn6FNPp9brpO7ctcA==",
+      "version": "3.4.3",
+      "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.3.tgz",
+      "integrity": "sha512-U7sxQk/n397Bmx4JHbJx/iSOOv5G+II3f1kpLpY2QeUv5DcPdcTsYLlusZfq1NthHS1c1cZoyFmmkex1rzke0A==",
       "dependencies": {
         "@alloc/quick-lru": "^5.2.0",
         "arg": "^5.0.2",
@@ -6643,7 +6809,7 @@
         "fast-glob": "^3.3.0",
         "glob-parent": "^6.0.2",
         "is-glob": "^4.0.3",
-        "jiti": "^1.19.1",
+        "jiti": "^1.21.0",
         "lilconfig": "^2.1.0",
         "micromatch": "^4.0.5",
         "normalize-path": "^3.0.0",
@@ -6756,6 +6922,14 @@
         "node": ">=8.0"
       }
     },
+    "node_modules/totalist": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz",
+      "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==",
+      "engines": {
+        "node": ">=6"
+      }
+    },
     "node_modules/trim-lines": {
       "version": "3.0.1",
       "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz",
@@ -6860,9 +7034,9 @@
       }
     },
     "node_modules/tsx": {
-      "version": "4.7.1",
-      "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.7.1.tgz",
-      "integrity": "sha512-8d6VuibXHtlN5E3zFkgY8u4DX7Y3Z27zvvPKVmLon/D4AjuKzarkUBTLDBgj9iTQ0hg5xM7c/mYiRVM+HETf0g==",
+      "version": "4.7.2",
+      "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.7.2.tgz",
+      "integrity": "sha512-BCNd4kz6fz12fyrgCTEdZHGJ9fWTGeUzXmQysh0RVocDY3h4frk05ZNCXSy4kIenF7y/QnrdiVpTsyNRn6vlAw==",
       "dev": true,
       "dependencies": {
         "esbuild": "~0.19.10",
@@ -6907,9 +7081,9 @@
       "integrity": "sha512-OJabfkAg1WLZSqJAJ0Z6Sdt3utnbzr/jh+NAHoyWHJe8CMSy79Gm085094M9nvTPy22KzTVn5Zq5mbapCI/hPA=="
     },
     "node_modules/typescript": {
-      "version": "5.4.2",
-      "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.2.tgz",
-      "integrity": "sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==",
+      "version": "5.4.4",
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.4.tgz",
+      "integrity": "sha512-dGE2Vv8cpVvw28v8HCPqyb08EzbBURxDpuhJvTrusShUfGnhHBafDsLdS1EhhxyL6BJQE+2cT3dDPAv+MQ6oLw==",
       "bin": {
         "tsc": "bin/tsc",
         "tsserver": "bin/tsserver"
@@ -7166,13 +7340,13 @@
       }
     },
     "node_modules/vite": {
-      "version": "5.1.6",
-      "resolved": "https://registry.npmjs.org/vite/-/vite-5.1.6.tgz",
-      "integrity": "sha512-yYIAZs9nVfRJ/AiOLCA91zzhjsHUgMjB+EigzFb6W2XTLO8JixBCKCjvhKZaye+NKYHCrkv3Oh50dH9EdLU2RA==",
+      "version": "5.2.8",
+      "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.8.tgz",
+      "integrity": "sha512-OyZR+c1CE8yeHw5V5t59aXsUPPVTHMDjEZz8MgguLL/Q7NblxhZUlTu9xSPqlsUO/y+X7dlU05jdhvyycD55DA==",
       "dependencies": {
-        "esbuild": "^0.19.3",
-        "postcss": "^8.4.35",
-        "rollup": "^4.2.0"
+        "esbuild": "^0.20.1",
+        "postcss": "^8.4.38",
+        "rollup": "^4.13.0"
       },
       "bin": {
         "vite": "bin/vite.js"
@@ -7219,6 +7393,388 @@
         }
       }
     },
+    "node_modules/vite/node_modules/@esbuild/aix-ppc64": {
+      "version": "0.20.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz",
+      "integrity": "sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==",
+      "cpu": [
+        "ppc64"
+      ],
+      "optional": true,
+      "os": [
+        "aix"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/android-arm": {
+      "version": "0.20.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.2.tgz",
+      "integrity": "sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==",
+      "cpu": [
+        "arm"
+      ],
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/android-arm64": {
+      "version": "0.20.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz",
+      "integrity": "sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==",
+      "cpu": [
+        "arm64"
+      ],
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/android-x64": {
+      "version": "0.20.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.2.tgz",
+      "integrity": "sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==",
+      "cpu": [
+        "x64"
+      ],
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/darwin-arm64": {
+      "version": "0.20.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz",
+      "integrity": "sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==",
+      "cpu": [
+        "arm64"
+      ],
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/darwin-x64": {
+      "version": "0.20.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz",
+      "integrity": "sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==",
+      "cpu": [
+        "x64"
+      ],
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/freebsd-arm64": {
+      "version": "0.20.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz",
+      "integrity": "sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==",
+      "cpu": [
+        "arm64"
+      ],
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/freebsd-x64": {
+      "version": "0.20.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz",
+      "integrity": "sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==",
+      "cpu": [
+        "x64"
+      ],
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/linux-arm": {
+      "version": "0.20.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz",
+      "integrity": "sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==",
+      "cpu": [
+        "arm"
+      ],
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/linux-arm64": {
+      "version": "0.20.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz",
+      "integrity": "sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==",
+      "cpu": [
+        "arm64"
+      ],
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/linux-ia32": {
+      "version": "0.20.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz",
+      "integrity": "sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==",
+      "cpu": [
+        "ia32"
+      ],
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/linux-loong64": {
+      "version": "0.20.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz",
+      "integrity": "sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==",
+      "cpu": [
+        "loong64"
+      ],
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/linux-mips64el": {
+      "version": "0.20.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz",
+      "integrity": "sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==",
+      "cpu": [
+        "mips64el"
+      ],
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/linux-ppc64": {
+      "version": "0.20.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz",
+      "integrity": "sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==",
+      "cpu": [
+        "ppc64"
+      ],
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/linux-riscv64": {
+      "version": "0.20.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz",
+      "integrity": "sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==",
+      "cpu": [
+        "riscv64"
+      ],
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/linux-s390x": {
+      "version": "0.20.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz",
+      "integrity": "sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==",
+      "cpu": [
+        "s390x"
+      ],
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/linux-x64": {
+      "version": "0.20.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz",
+      "integrity": "sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==",
+      "cpu": [
+        "x64"
+      ],
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/netbsd-x64": {
+      "version": "0.20.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz",
+      "integrity": "sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==",
+      "cpu": [
+        "x64"
+      ],
+      "optional": true,
+      "os": [
+        "netbsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/openbsd-x64": {
+      "version": "0.20.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz",
+      "integrity": "sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==",
+      "cpu": [
+        "x64"
+      ],
+      "optional": true,
+      "os": [
+        "openbsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/sunos-x64": {
+      "version": "0.20.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz",
+      "integrity": "sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==",
+      "cpu": [
+        "x64"
+      ],
+      "optional": true,
+      "os": [
+        "sunos"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/win32-arm64": {
+      "version": "0.20.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz",
+      "integrity": "sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/win32-ia32": {
+      "version": "0.20.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz",
+      "integrity": "sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==",
+      "cpu": [
+        "ia32"
+      ],
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/win32-x64": {
+      "version": "0.20.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz",
+      "integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==",
+      "cpu": [
+        "x64"
+      ],
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite/node_modules/esbuild": {
+      "version": "0.20.2",
+      "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz",
+      "integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==",
+      "hasInstallScript": true,
+      "bin": {
+        "esbuild": "bin/esbuild"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "optionalDependencies": {
+        "@esbuild/aix-ppc64": "0.20.2",
+        "@esbuild/android-arm": "0.20.2",
+        "@esbuild/android-arm64": "0.20.2",
+        "@esbuild/android-x64": "0.20.2",
+        "@esbuild/darwin-arm64": "0.20.2",
+        "@esbuild/darwin-x64": "0.20.2",
+        "@esbuild/freebsd-arm64": "0.20.2",
+        "@esbuild/freebsd-x64": "0.20.2",
+        "@esbuild/linux-arm": "0.20.2",
+        "@esbuild/linux-arm64": "0.20.2",
+        "@esbuild/linux-ia32": "0.20.2",
+        "@esbuild/linux-loong64": "0.20.2",
+        "@esbuild/linux-mips64el": "0.20.2",
+        "@esbuild/linux-ppc64": "0.20.2",
+        "@esbuild/linux-riscv64": "0.20.2",
+        "@esbuild/linux-s390x": "0.20.2",
+        "@esbuild/linux-x64": "0.20.2",
+        "@esbuild/netbsd-x64": "0.20.2",
+        "@esbuild/openbsd-x64": "0.20.2",
+        "@esbuild/sunos-x64": "0.20.2",
+        "@esbuild/win32-arm64": "0.20.2",
+        "@esbuild/win32-ia32": "0.20.2",
+        "@esbuild/win32-x64": "0.20.2"
+      }
+    },
     "node_modules/vitefu": {
       "version": "0.2.5",
       "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-0.2.5.tgz",
@@ -7233,9 +7789,9 @@
       }
     },
     "node_modules/volar-service-css": {
-      "version": "0.0.33",
-      "resolved": "https://registry.npmjs.org/volar-service-css/-/volar-service-css-0.0.33.tgz",
-      "integrity": "sha512-CQt4s/3ltH8clGD+GNkztKLLsifHDO9/2VTibgyj/os90uHJ/b4uiY0F0XbyEj493M9c10xhl+It6quLt2Vz1w==",
+      "version": "0.0.34",
+      "resolved": "https://registry.npmjs.org/volar-service-css/-/volar-service-css-0.0.34.tgz",
+      "integrity": "sha512-C7ua0j80ZD7bsgALAz/cA1bykPehoIa5n+3+Ccr+YLpj0fypqw9iLUmGLX11CqzqNCO2XFGe/1eXB/c+SWrF/g==",
       "dependencies": {
         "vscode-css-languageservice": "^6.2.10",
         "vscode-languageserver-textdocument": "^1.0.11",
@@ -7251,9 +7807,9 @@
       }
     },
     "node_modules/volar-service-emmet": {
-      "version": "0.0.33",
-      "resolved": "https://registry.npmjs.org/volar-service-emmet/-/volar-service-emmet-0.0.33.tgz",
-      "integrity": "sha512-wPsqD7YXArQo7IIEfZJ2kbKWBtqsIUsF0Hjqm9xwQnsuOzahGRNw/VxCJggLt+AjiK0c/ucCvaNTb8j0SPTiOQ==",
+      "version": "0.0.34",
+      "resolved": "https://registry.npmjs.org/volar-service-emmet/-/volar-service-emmet-0.0.34.tgz",
+      "integrity": "sha512-ubQvMCmHPp8Ic82LMPkgrp9ot+u2p/RDd0RyT0EykRkZpWsagHUF5HWkVheLfiMyx2rFuWx/+7qZPOgypx6h6g==",
       "dependencies": {
         "@vscode/emmet-helper": "^2.9.2",
         "vscode-html-languageservice": "^5.1.0"
@@ -7268,9 +7824,9 @@
       }
     },
     "node_modules/volar-service-html": {
-      "version": "0.0.33",
-      "resolved": "https://registry.npmjs.org/volar-service-html/-/volar-service-html-0.0.33.tgz",
-      "integrity": "sha512-kthyHYcjOjREqTXg/rEPT8AascgjX+cuImHuu+IbHCTM0FnXGRT/vUfSp+f2l+k0tJkQHsx5NIv+xOxrrNv9Yg==",
+      "version": "0.0.34",
+      "resolved": "https://registry.npmjs.org/volar-service-html/-/volar-service-html-0.0.34.tgz",
+      "integrity": "sha512-kMEneea1tQbiRcyKavqdrSVt8zV06t+0/3pGkjO3gV6sikXTNShIDkdtB4Tq9vE2cQdM50TuS7utVV7iysUxHw==",
       "dependencies": {
         "vscode-html-languageservice": "^5.1.0",
         "vscode-languageserver-textdocument": "^1.0.11",
@@ -7286,9 +7842,9 @@
       }
     },
     "node_modules/volar-service-prettier": {
-      "version": "0.0.33",
-      "resolved": "https://registry.npmjs.org/volar-service-prettier/-/volar-service-prettier-0.0.33.tgz",
-      "integrity": "sha512-G4i4ugev284B0/sbfggxE6BQugbz8aWBrFbgMbihz9jZ5vC8HEYXT42Dm/8PITjsJTxQM6QtHzyqa6+Adb7VHQ==",
+      "version": "0.0.34",
+      "resolved": "https://registry.npmjs.org/volar-service-prettier/-/volar-service-prettier-0.0.34.tgz",
+      "integrity": "sha512-BNfJ8FwfPi1Wm/JkuzNjraOLdtKieGksNT/bDyquygVawv1QUzO2HB1hiMKfZGdcSFG5ZL9R0j7bBfRTfXA2gg==",
       "dependencies": {
         "vscode-uri": "^3.0.8"
       },
@@ -7306,9 +7862,9 @@
       }
     },
     "node_modules/volar-service-typescript": {
-      "version": "0.0.33",
-      "resolved": "https://registry.npmjs.org/volar-service-typescript/-/volar-service-typescript-0.0.33.tgz",
-      "integrity": "sha512-ZHk4DXQAcYUMWMkpYzN6Aver2SahOGQ2KsEZDZKaKm2WWKaAP3TWAnDLa+t2rr1HlUz95n7liUW5qE0cDo/cuw==",
+      "version": "0.0.34",
+      "resolved": "https://registry.npmjs.org/volar-service-typescript/-/volar-service-typescript-0.0.34.tgz",
+      "integrity": "sha512-NbAry0w8ZXFgGsflvMwmPDCzgJGx3C+eYxFEbldaumkpTAJiywECWiUbPIOfmEHgpOllUKSnhwtLlWFK4YnfQg==",
       "dependencies": {
         "path-browserify": "^1.0.1",
         "semver": "^7.5.4",
@@ -7326,9 +7882,9 @@
       }
     },
     "node_modules/volar-service-typescript-twoslash-queries": {
-      "version": "0.0.33",
-      "resolved": "https://registry.npmjs.org/volar-service-typescript-twoslash-queries/-/volar-service-typescript-twoslash-queries-0.0.33.tgz",
-      "integrity": "sha512-wJXrLYzh8OmUe3qP9s6tnNFFieUk2ELdH+8pzBZLCvZM2hjMTr9TejAoYFpZbxLKeKi7ZtJbvkEsYsOJkLyiSA==",
+      "version": "0.0.34",
+      "resolved": "https://registry.npmjs.org/volar-service-typescript-twoslash-queries/-/volar-service-typescript-twoslash-queries-0.0.34.tgz",
+      "integrity": "sha512-XAY2YtWKUp6ht89gxt3L5Dr46LU45d/VlBkj1KXUwNlinpoWiGN4Nm3B6DRF3VoBThAnQgm4c7WD0S+5yTzh+w==",
       "peerDependencies": {
         "@volar/language-service": "~2.1.0"
       },
@@ -7339,9 +7895,9 @@
       }
     },
     "node_modules/vscode-css-languageservice": {
-      "version": "6.2.12",
-      "resolved": "https://registry.npmjs.org/vscode-css-languageservice/-/vscode-css-languageservice-6.2.12.tgz",
-      "integrity": "sha512-PS9r7HgNjqzRl3v91sXpCyZPc8UDotNo6gntFNtGCKPhGA9Frk7g/VjX1Mbv3F00pn56D+rxrFzR9ep4cawOgA==",
+      "version": "6.2.13",
+      "resolved": "https://registry.npmjs.org/vscode-css-languageservice/-/vscode-css-languageservice-6.2.13.tgz",
+      "integrity": "sha512-2rKWXfH++Kxd9Z4QuEgd1IF7WmblWWU7DScuyf1YumoGLkY9DW6wF/OTlhOyO2rN63sWHX2dehIpKBbho4ZwvA==",
       "dependencies": {
         "@vscode/l10n": "^0.0.18",
         "vscode-languageserver-textdocument": "^1.0.11",
@@ -7355,9 +7911,9 @@
       "integrity": "sha512-KYSIHVmslkaCDyw013pphY+d7x1qV8IZupYfeIfzNA+nsaWHbn5uPuQRvdRFsa9zFzGeudPuoGoZ1Op4jrJXIQ=="
     },
     "node_modules/vscode-html-languageservice": {
-      "version": "5.1.2",
-      "resolved": "https://registry.npmjs.org/vscode-html-languageservice/-/vscode-html-languageservice-5.1.2.tgz",
-      "integrity": "sha512-wkWfEx/IIR3s2P5yD4aTGHiOb8IAzFxgkSt1uSC3itJ4oDAm23yG7o0L29JljUdnXDDgLafPAvhv8A2I/8riHw==",
+      "version": "5.2.0",
+      "resolved": "https://registry.npmjs.org/vscode-html-languageservice/-/vscode-html-languageservice-5.2.0.tgz",
+      "integrity": "sha512-cdNMhyw57/SQzgUUGSIMQ66jikqEN6nBNyhx5YuOyj9310+eY9zw8Q0cXpiKzDX8aHYFewQEXRnigl06j/TVwQ==",
       "dependencies": {
         "@vscode/l10n": "^0.0.18",
         "vscode-languageserver-textdocument": "^1.0.11",
diff --git a/package.json b/package.json
index 1b754a1..9744152 100644
--- a/package.json
+++ b/package.json
@@ -1,37 +1,39 @@
 {
   "name": "gallery-badmanners-xyz",
   "type": "module",
-  "version": "1.2.2",
+  "version": "1.3.0",
   "scripts": {
     "dev": "astro dev",
     "start": "astro dev",
-    "build": "astro check && astro build",
+    "build": "astro check --minimumSeverity warning && astro build",
     "preview": "astro preview",
     "astro": "astro",
     "prettier": "prettier . --write",
     "export-story": "tsx scripts/export-story.ts"
   },
   "dependencies": {
-    "@astrojs/check": "^0.5.9",
+    "@astrojs/check": "^0.5.10",
     "@astrojs/rss": "^4.0.5",
     "@astrojs/tailwind": "^5.1.0",
     "@astropub/md": "^0.4.0",
-    "@tailwindcss/typography": "^0.5.10",
+    "@tailwindcss/typography": "^0.5.12",
     "@types/sanitize-html": "^2.11.0",
-    "astro": "^4.5.4",
+    "astro": "^4.5.16",
+    "astro-pagefind": "^1.5.0",
     "github-slugger": "^2.0.0",
     "marked": "^12.0.1",
+    "pagefind": "^1.1.0",
     "sanitize-html": "^2.13.0",
-    "tailwindcss": "^3.4.1",
+    "tailwindcss": "^3.4.3",
     "tiny-decode": "^0.1.3",
-    "typescript": "^5.4.2"
+    "typescript": "^5.4.4"
   },
   "devDependencies": {
     "commander": "^12.0.0",
     "fetch-retry": "^6.0.0",
     "prettier": "^3.2.5",
     "prettier-plugin-astro": "^0.13.0",
-    "prettier-plugin-tailwindcss": "^0.5.12",
-    "tsx": "^4.7.1"
+    "prettier-plugin-tailwindcss": "^0.5.13",
+    "tsx": "^4.7.2"
   }
 }
diff --git a/src/components/Navigation.astro b/src/components/Navigation.astro
index fd458de..633d13e 100644
--- a/src/components/Navigation.astro
+++ b/src/components/Navigation.astro
@@ -28,5 +28,11 @@
         href="/tags">Tags</a
       >
     </li>
+    <li>
+      <a
+        class="hover:text-green-800 hover:underline focus:text-green-800 focus:underline dark:hover:text-bm-300 dark:focus:text-bm-300"
+        href="/search">Search</a
+      >
+    </li>
   </ul>
 </nav>
diff --git a/src/layouts/GalleryLayout.astro b/src/layouts/GalleryLayout.astro
index 48310c0..7583b74 100644
--- a/src/layouts/GalleryLayout.astro
+++ b/src/layouts/GalleryLayout.astro
@@ -6,19 +6,21 @@ import logoBM from "../assets/images/logo_bm.png";
 
 type Props = {
   pageTitle?: string;
+  enablePagefind?: boolean;
 };
 
-const { pageTitle } = Astro.props;
+const { pageTitle, enablePagefind } = Astro.props;
 const logo = await getImage({ src: logoBM, width: 192 });
 ---
 
 <BaseLayout pageTitle={pageTitle}>
   <Fragment slot="head">
-    <meta content={pageTitle || "Bad Manners"} property="og:title" />
+    <meta property="og:title" content={pageTitle || "Bad Manners"} />
     <slot name="head-description" />
-    <meta content={Astro.url} property="og:url" />
-    <meta content={logo.src} property="og:image" />
-    <meta content="#7DD05A" data-react-helmet="true" name="theme-color" />
+    <meta property="og:url" content={Astro.url} />
+    <meta property="og:image" content={logo.src} />
+    <meta property="og:image:alt" content="Logo for Bad Manners" />
+    <meta name="theme-color" content="#7DD05A" data-react-helmet="true" />
   </Fragment>
   <div
     class="flex min-h-screen flex-col bg-stone-200 text-stone-800 md:flex-row dark:bg-stone-800 dark:text-stone-200 print:bg-none"
@@ -68,7 +70,7 @@ const logo = await getImage({ src: logoBM, width: 192 });
         </button>
       </div>
     </div>
-    <main class="ml-0 max-w-6xl px-2 pb-12 pt-4 md:ml-60 md:px-4 print:pb-0">
+    <main class="ml-0 max-w-6xl px-2 pb-12 pt-4 md:ml-60 md:px-4 print:pb-0" data-pagefind-body={enablePagefind ? "" : undefined}>
       <slot />
     </main>
   </div>
diff --git a/src/layouts/GameLayout.astro b/src/layouts/GameLayout.astro
index cd178a4..3db7952 100644
--- a/src/layouts/GameLayout.astro
+++ b/src/layouts/GameLayout.astro
@@ -58,11 +58,14 @@ const thumbnail =
 
 <BaseLayout pageTitle={props.title}>
   <Fragment slot="head">
-    <meta content={props.title} property="og:title" />
-    <meta content={props.contentWarning} property="og:description" />
-    <meta content={Astro.url} property="og:url" />
-    {thumbnail ? <meta content={thumbnail.src} property="og:image" /> : null}
-    <meta content="#7DD05A" data-react-helmet="true" name="theme-color" />
+    <meta property="og:title" content={props.title} data-pagefind-meta="title[content]" />
+    <meta property="og:description" content={props.contentWarning} />
+    <meta property="og:url" content={Astro.url} data-pagefind-meta="url[content]" />
+    {thumbnail ? <Fragment>
+      <meta content={thumbnail.src} property="og:image" data-pagefind-meta="image[content]" />
+      <meta property="og:image:alt" content={`Cover art for ${props.title}`} data-pagefind-meta="image_alt[content]" />
+    </Fragment> : null}
+    <meta name="theme-color" content="#7DD05A" data-react-helmet="true" />
   </Fragment>
   <div
     id="top"
@@ -118,6 +121,8 @@ const thumbnail =
     </div>
     <main
       class="mx-auto max-w-3xl rounded-lg bg-stone-50 px-2 pb-4 pt-1 shadow-sm dark:bg-stone-900 print:max-w-full print:bg-none print:shadow-none"
+      data-pagefind-body
+      data-pagefind-meta="type:game"
     >
       <h1 id="game-title" class="px-2 pt-2 font-serif text-3xl font-semibold text-stone-800 dark:text-stone-100">
         {props.title}
@@ -151,6 +156,7 @@ const thumbnail =
               width={props.thumbnailWidth}
               height={props.thumbnailHeight}
               class="mx-auto my-5 shadow-lg"
+              data-pagefind-meta="image[src],image_alt[alt]"
             />
           </Fragment>
         ) : null
@@ -180,6 +186,8 @@ const thumbnail =
               day: "numeric",
               year: "numeric",
             })}
+            data-pagefind-index-attrs="aria-description"
+            data-pagefind-meta={`date:${props.pubDate.toISOString().slice(undefined, 10)}`}
           >
             {t(props.lang, "story/publish_date", props.pubDate.toISOString().slice(undefined, 10))}
           </p>
diff --git a/src/layouts/StoryLayout.astro b/src/layouts/StoryLayout.astro
index 449e11a..195c404 100644
--- a/src/layouts/StoryLayout.astro
+++ b/src/layouts/StoryLayout.astro
@@ -68,11 +68,14 @@ const thumbnail =
 
 <BaseLayout pageTitle={props.title}>
   <Fragment slot="head">
-    <meta content={props.title} property="og:title" />
-    <meta content={`Word count: ${props.wordCount}. ${props.contentWarning}`} property="og:description" />
-    <meta content={Astro.url} property="og:url" />
-    {thumbnail ? <meta content={thumbnail.src} property="og:image" /> : null}
-    <meta content="#7DD05A" data-react-helmet="true" name="theme-color" />
+    <meta property="og:title" content={props.title} data-pagefind-meta="title[content]" />
+    <meta property="og:description" content={`Word count: ${props.wordCount}. ${props.contentWarning}`} />
+    <meta property="og:url" content={Astro.url} data-pagefind-meta="url[content]" />
+    {thumbnail ? <Fragment>
+      <meta content={thumbnail.src} property="og:image" data-pagefind-meta="image[content]" />
+      <meta property="og:image:alt" content={`Cover art for ${props.shortTitle || props.title}`} data-pagefind-meta="image_alt[content]" />
+    </Fragment> : null}
+    <meta name="theme-color" content="#7DD05A" data-react-helmet="true" />
   </Fragment>
   <div
     id="top"
@@ -130,6 +133,8 @@ const thumbnail =
     </div>
     <main
       class="mx-auto max-w-3xl rounded-lg bg-stone-50 px-2 pb-4 pt-1 shadow-sm dark:bg-stone-900 print:max-w-full print:bg-none print:shadow-none"
+      data-pagefind-body
+      data-pagefind-meta="type:story"
     >
       {
         prev || next ? (
@@ -210,7 +215,7 @@ const thumbnail =
             <img
               loading="eager"
               src={thumbnail.src}
-              alt={`Cover art for ${props.title}`}
+              alt={`Cover art for ${props.shortTitle || props.title}`}
               width={props.thumbnailWidth}
               height={props.thumbnailHeight}
               class="mx-auto my-5 shadow-lg"
@@ -243,6 +248,8 @@ const thumbnail =
               day: "numeric",
               year: "numeric",
             })}
+            data-pagefind-index-attrs="aria-description"
+            data-pagefind-meta={`date:${props.pubDate.toISOString().slice(undefined, 10)}`}
           >
             {t(props.lang, "story/publish_date", props.pubDate.toISOString().slice(undefined, 10))}
           </p>
diff --git a/src/pages/games.astro b/src/pages/games.astro
index faf0e5e..a293e89 100644
--- a/src/pages/games.astro
+++ b/src/pages/games.astro
@@ -9,7 +9,7 @@ const games = (await getCollection("games", (game) => !game.data.isDraft)).sort(
 ---
 
 <GalleryLayout pageTitle="Games">
-  <meta slot="head-description" content="Bad Manners || A game that I've gone and done." property="og:description" />
+  <meta slot="head-description" property="og:description" content="Bad Manners || A game that I've gone and done." />
   <h1 class="m-2 text-2xl font-semibold text-stone-800 dark:text-stone-100">Games</h1>
   <p class="my-4">A game that I've gone and done.</p>
   <ul class="my-6 flex flex-wrap items-start justify-center gap-4 text-center md:justify-normal">
diff --git a/src/pages/index.astro b/src/pages/index.astro
index 8b44d83..79d15f5 100644
--- a/src/pages/index.astro
+++ b/src/pages/index.astro
@@ -42,7 +42,7 @@ const latestItems: LatestItemsEntry[] = [
 ---
 
 <GalleryLayout pageTitle="Gallery">
-  <meta slot="head-description" content="Bad Manners || Welcome to my gallery!" property="og:description" />
+  <meta slot="head-description" property="og:description" content="Bad Manners || Welcome to my gallery!" />
   <h1 class="m-2 text-2xl font-semibold text-stone-800 dark:text-stone-100">My gallery</h1>
   <p class="my-4">Welcome to my gallery! Use the navigation menu to navigate through my content.</p>
   <ul class="list-disc pl-8">
diff --git a/src/pages/search.astro b/src/pages/search.astro
new file mode 100644
index 0000000..9ca1bf5
--- /dev/null
+++ b/src/pages/search.astro
@@ -0,0 +1,11 @@
+---
+import SearchComponent from "astro-pagefind/components/Search";
+import GalleryLayout from "../layouts/GalleryLayout.astro";
+
+---
+
+<GalleryLayout pageTitle="Search">
+  <meta slot="head-description" property="og:description" content="Bad Manners || Search" />
+  <h1 class="m-2 text-2xl font-semibold text-stone-800 dark:text-stone-100">Search</h1>
+  <SearchComponent id="search" className="my-4 pagefind-ui" />
+</GalleryLayout>
\ No newline at end of file
diff --git a/src/pages/stories/[page].astro b/src/pages/stories/[page].astro
index 4cebc20..46d81eb 100644
--- a/src/pages/stories/[page].astro
+++ b/src/pages/stories/[page].astro
@@ -21,7 +21,7 @@ const totalPages = Math.ceil(page.total / page.size);
 ---
 
 <GalleryLayout pageTitle="Stories">
-  <meta slot="head-description" content={`Bad Manners || ${page.total} stories.`} property="og:description" />
+  <meta slot="head-description" property="og:description" content={`Bad Manners || ${page.total} stories.`} />
   <h1 class="m-2 text-2xl font-semibold text-stone-800 dark:text-stone-100">Stories</h1>
   <p class="my-4">The bulk of my content!</p>
   <p class="text-center font-light text-stone-950 dark:text-white">
diff --git a/src/pages/stories/the-lost-of-the-marshes.astro b/src/pages/stories/the-lost-of-the-marshes.astro
index a1484b2..69d33e4 100644
--- a/src/pages/stories/the-lost-of-the-marshes.astro
+++ b/src/pages/stories/the-lost-of-the-marshes.astro
@@ -15,16 +15,12 @@ const bonusChapters = stories
 const mainChaptersWithSummaries = mainChapters.filter((story) => story.data.summary);
 ---
 
-<GalleryLayout pageTitle={series.data.name}>
-  <meta
-    slot="head-description"
-    content={`Bad Manners || The story of Quince, Nikili, and Suu.`}
-    property="og:description"
-  />
+<GalleryLayout pageTitle={series.data.name} enablePagefind={true}>
+  <meta slot="head-description" property="og:description" content="Bad Manners || The story of Quince, Nikili, and Suu."/>
   <h1 class="m-2 text-2xl font-semibold text-stone-800 dark:text-stone-100">{series.data.name}</h1>
   <p class="my-4">This is the main hub for the story of Quince, Nikili, and Suu, as well as all bonus content.</p>
   <section class="my-2" aria-labelledby="main-chapters">
-    <h2 id="main-chapters" class="p-2 text-xl font-semibold text-stone-800 dark:text-stone-100">Main chapters</h2>
+    <h2 id="main-chapters" class="p-2 text-xl font-semibold text-stone-800 dark:text-stone-100" data-pagefind-meta="type:series">Main chapters</h2>
     <details
       class="mx-3 mb-6 mt-1 rounded-lg border border-stone-400 bg-stone-300 dark:border-stone-500 dark:bg-stone-700"
     >
@@ -99,6 +95,7 @@ const mainChaptersWithSummaries = mainChapters.filter((story) => story.data.summ
       class="mx-auto w-full max-w-4xl break-before-page"
       src={mapImage}
       alt="A geopolitical map for the setting of The Lost of the Marshes"
+      data-pagefind-meta="image[src],image_alt[alt]"
     />
   </section>
 </GalleryLayout>
diff --git a/src/pages/tags.astro b/src/pages/tags.astro
index ebd1ca6..4864988 100644
--- a/src/pages/tags.astro
+++ b/src/pages/tags.astro
@@ -82,11 +82,11 @@ if (uncategorizedTagsSet.size > 0) {
 }
 ---
 
-<GalleryLayout pageTitle={`Tags`}>
+<GalleryLayout pageTitle="Tags">
   <meta
+    property="og:description"  
     slot="head-description"
     content="Bad Manners || Find all content with a specific tag."
-    property="og:description"
   />
   <h1 class="m-2 text-2xl font-semibold text-stone-800 dark:text-stone-100">All available tags</h1>
   <p class="my-4">You can find all content with a specific tag by selecting it below from the appropriate category.</p>
diff --git a/src/styles/base.css b/src/styles/base.css
index 5a920b7..b21b740 100644
--- a/src/styles/base.css
+++ b/src/styles/base.css
@@ -3,7 +3,37 @@
 @tailwind utilities;
 
 @layer components {
-  .text-link {
+  .text-link, .pagefind-ui .pagefind-ui__result-link {
     @apply text-stone-800 hover:text-bm-500 focus:text-bm-500 dark:text-zinc-300 dark:hover:text-bm-400 dark:focus:text-bm-400;
   }
+
+  .pagefind-ui {
+    --pagefind-ui-primary: theme(colors.stone.800);
+    --pagefind-ui-text: theme(colors.stone.900);
+    --pagefind-ui-background: theme(colors.stone.50);
+    --pagefind-ui-border: theme(colors.stone.400);
+    --pagefind-ui-tag: theme(colors.bm.300);
+
+    --pagefind-ui-scale: 0.8;
+    --pagefind-ui-font: theme(fontFamily.sans);
+    --pagefind-ui-border-width: 1px;
+    --pagefind-ui-border-radius: 8px;
+    --pagefind-ui-image-border-radius: 0px;
+  }
+
+  @media screen(md) {
+    .pagefind-ui {
+      --pagefind-ui-scale: 0.9;
+    }
+  }
+
+  @media not print {
+    .dark .pagefind-ui {
+      --pagefind-ui-primary: theme(colors.stone.200);
+      --pagefind-ui-text: theme(colors.stone.100);
+      --pagefind-ui-background: theme(colors.stone.700);
+      --pagefind-ui-border: theme(colors.stone.500);
+      --pagefind-ui-tag: theme(colors.green.800);
+    }
+  }
 }