From 69349319eb5c70aa644a7446d73674503f09465b Mon Sep 17 00:00:00 2001
From: Puru Vijay <47742487+PuruVJ@users.noreply.github.com>
Date: Tue, 7 Feb 2023 16:28:30 +0530
Subject: [PATCH] feat: multi-page docs (#8253)

* Rename markdown to blog

* Multi-page docs

* Ignore missing ID for now

* heading

* Add code snippets(temporary)

* /docs redirects to introduction

* Remove ukraine petition

* OnThisPage

* Remove console.log

* Update deps

* Fix accessibility warning

* Update site-kit
---
 sites/svelte.dev/package-lock.json            | 1348 +++++++++++------
 sites/svelte.dev/package.json                 |   20 +-
 sites/svelte.dev/src/lib/server/blog/index.js |   73 +
 .../lib/server/{markdown => blog}/marked.js   |    0
 .../lib/server/{markdown => blog}/types.d.ts  |    0
 sites/svelte.dev/src/lib/server/docs/index.js |  570 +++++++
 sites/svelte.dev/src/lib/server/docs/types.ts |   13 +
 .../src/lib/server/markdown/index.js          |  232 ++-
 sites/svelte.dev/src/routes/+layout.svelte    |   68 +-
 .../_components/WhosUsingSvelte/index.svelte  |    2 +-
 .../src/routes/blog/+page.server.js           |    4 +-
 .../src/routes/blog/[slug]/+page.server.js    |    4 +-
 .../routes/blog/[slug]/card.png/+server.js    |   17 +-
 .../src/routes/blog/rss.xml/+server.js        |   10 +-
 .../src/routes/docs/+layout.server.js         |   23 +
 .../svelte.dev/src/routes/docs/+layout.svelte |  337 +++++
 sites/svelte.dev/src/routes/docs/+page.js     |   13 +-
 sites/svelte.dev/src/routes/docs/+page.svelte |   43 -
 .../src/routes/docs/Contents.svelte           |  137 ++
 .../src/routes/docs/[slug]/+page.server.js    |   24 +
 .../src/routes/docs/[slug]/+page.svelte       |   16 +
 .../src/routes/docs/[slug]/OnThisPage.svelte  |  141 ++
 sites/svelte.dev/svelte.config.js             |    8 +-
 sites/svelte.dev/tsconfig.json                |    5 +
 24 files changed, 2416 insertions(+), 692 deletions(-)
 create mode 100644 sites/svelte.dev/src/lib/server/blog/index.js
 rename sites/svelte.dev/src/lib/server/{markdown => blog}/marked.js (100%)
 rename sites/svelte.dev/src/lib/server/{markdown => blog}/types.d.ts (100%)
 create mode 100644 sites/svelte.dev/src/lib/server/docs/index.js
 create mode 100644 sites/svelte.dev/src/lib/server/docs/types.ts
 create mode 100644 sites/svelte.dev/src/routes/docs/+layout.server.js
 create mode 100644 sites/svelte.dev/src/routes/docs/+layout.svelte
 delete mode 100644 sites/svelte.dev/src/routes/docs/+page.svelte
 create mode 100644 sites/svelte.dev/src/routes/docs/Contents.svelte
 create mode 100644 sites/svelte.dev/src/routes/docs/[slug]/+page.server.js
 create mode 100644 sites/svelte.dev/src/routes/docs/[slug]/+page.svelte
 create mode 100644 sites/svelte.dev/src/routes/docs/[slug]/OnThisPage.svelte

diff --git a/sites/svelte.dev/package-lock.json b/sites/svelte.dev/package-lock.json
index 12ff957e2d..904b8c3fe4 100644
--- a/sites/svelte.dev/package-lock.json
+++ b/sites/svelte.dev/package-lock.json
@@ -8,7 +8,7 @@
       "name": "svelte.dev",
       "version": "1.0.0",
       "dependencies": {
-        "@supabase/supabase-js": "^2.7.0",
+        "@supabase/supabase-js": "^2.7.1",
         "@sveltejs/repl": "0.0.3",
         "cookie": "^0.5.0",
         "devalue": "^4.2.3",
@@ -17,42 +17,34 @@
         "sourcemap-codec": "^1.4.8"
       },
       "devDependencies": {
-        "@resvg/resvg-js": "^2.2.0",
-        "@sveltejs/adapter-auto": "^1.0.2",
-        "@sveltejs/kit": "^1.3.10",
-        "@sveltejs/site-kit": "^3.2.1",
+        "@resvg/resvg-js": "^2.3.1",
+        "@sveltejs/adapter-auto": "^2.0.0",
+        "@sveltejs/kit": "^1.5.0",
+        "@sveltejs/site-kit": "^3.2.2",
         "@sveltejs/vite-plugin-svelte": "^2.0.2",
+        "@types/marked": "^4.0.8",
+        "@types/prismjs": "^1.26.0",
         "degit": "^2.8.4",
         "dotenv": "^16.0.3",
-        "jimp": "^0.16.2",
+        "jimp": "^0.22.4",
         "marked": "^4.2.12",
         "node-fetch": "^3.3.0",
         "prettier": "^2.8.3",
         "prettier-plugin-svelte": "^2.9.0",
         "prism-svelte": "^0.5.0",
         "prismjs": "^1.29.0",
-        "satori": "^0.1.2",
+        "satori": "^0.2.3",
         "satori-html": "^0.3.2",
         "shelljs": "^0.8.5",
+        "shiki": "^0.14.0",
+        "shiki-twoslash": "^3.1.0",
         "svelte": "^3.55.1",
         "svelte-check": "^3.0.3",
         "typescript": "^4.9.5",
-        "vite": "^4.0.4",
+        "vite": "^4.1.1",
         "vite-imagetools": "^4.0.18"
       }
     },
-    "node_modules/@babel/runtime": {
-      "version": "7.20.13",
-      "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.13.tgz",
-      "integrity": "sha512-gt3PKXs0DBoL9xCvOIIZ2NEqAGZqHjAnmVbfQtB620V0uReIQutpel14KcneZuer7UioY8ALKZ7iocavvzTNFA==",
-      "dev": true,
-      "dependencies": {
-        "regenerator-runtime": "^0.13.11"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
     "node_modules/@esbuild/android-arm": {
       "version": "0.16.17",
       "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.16.17.tgz",
@@ -406,13 +398,12 @@
       }
     },
     "node_modules/@jimp/bmp": {
-      "version": "0.16.2",
-      "resolved": "https://registry.npmjs.org/@jimp/bmp/-/bmp-0.16.2.tgz",
-      "integrity": "sha512-4g9vW45QfMoGhLVvaFj26h4e7cC+McHUQwyFQmNTLW4FfC1OonN9oUr2m/FEDGkTYKR7aqdXR5XUqqIkHWLaFw==",
+      "version": "0.22.4",
+      "resolved": "https://registry.npmjs.org/@jimp/bmp/-/bmp-0.22.4.tgz",
+      "integrity": "sha512-ZDwQ/tLihpZuTCFGGa0zcyZIWHfhvHkrdbsoHUY0GG/JpH/y2xzlm2I48/TicCpoujN8oGKLHIJje0HmVX3xaA==",
       "dev": true,
       "dependencies": {
-        "@babel/runtime": "^7.7.2",
-        "@jimp/utils": "^0.16.2",
+        "@jimp/utils": "^0.22.4",
         "bmp-js": "^0.1.0"
       },
       "peerDependencies": {
@@ -420,42 +411,53 @@
       }
     },
     "node_modules/@jimp/core": {
-      "version": "0.16.2",
-      "resolved": "https://registry.npmjs.org/@jimp/core/-/core-0.16.2.tgz",
-      "integrity": "sha512-dp7HcyUMzjXphXYodI6PaXue+I9PXAavbb+AN+1XqFbotN22Z12DosNPEyy+UhLY/hZiQQqUkEaJHkvV31rs+w==",
+      "version": "0.22.4",
+      "resolved": "https://registry.npmjs.org/@jimp/core/-/core-0.22.4.tgz",
+      "integrity": "sha512-K7guEYpXV44SCLR35QdPyKqF+mFZaEUAqiSL8qQ/F4N4Ws9JkPzFI/qYTjOkDoKxSWkXlKnlsk1sfMzy0yqA5g==",
       "dev": true,
       "dependencies": {
-        "@babel/runtime": "^7.7.2",
-        "@jimp/utils": "^0.16.2",
+        "@jimp/utils": "^0.22.4",
         "any-base": "^1.1.0",
         "buffer": "^5.2.0",
         "exif-parser": "^0.1.12",
-        "file-type": "^9.0.0",
-        "load-bmfont": "^1.3.1",
-        "mkdirp": "^0.5.1",
-        "phin": "^2.9.1",
+        "file-type": "^16.5.4",
+        "isomorphic-fetch": "^3.0.0",
+        "mkdirp": "^2.1.3",
         "pixelmatch": "^4.0.2",
-        "tinycolor2": "^1.4.1"
+        "tinycolor2": "^1.6.0"
+      }
+    },
+    "node_modules/@jimp/core/node_modules/mkdirp": {
+      "version": "2.1.3",
+      "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-2.1.3.tgz",
+      "integrity": "sha512-sjAkg21peAG9HS+Dkx7hlG9Ztx7HLeKnvB3NQRcu/mltCVmvkF0pisbiTSfDVYTT86XEfZrTUosLdZLStquZUw==",
+      "dev": true,
+      "bin": {
+        "mkdirp": "dist/cjs/src/bin.js"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
       }
     },
     "node_modules/@jimp/custom": {
-      "version": "0.16.2",
-      "resolved": "https://registry.npmjs.org/@jimp/custom/-/custom-0.16.2.tgz",
-      "integrity": "sha512-GtNwOs4hcVS2GIbqRUf42rUuX07oLB92cj7cqxZb0ZGWwcwhnmSW0TFLAkNafXmqn9ug4VTpNvcJSUdiuECVKg==",
+      "version": "0.22.4",
+      "resolved": "https://registry.npmjs.org/@jimp/custom/-/custom-0.22.4.tgz",
+      "integrity": "sha512-k9m/RfxjPjklUsgZ2nszlyNkodUG/4xlxlif70UELhxW8bdqZqqlQGzwA9p+PUiSnlSJYZjL6q+P8cd7yj1ggA==",
       "dev": true,
       "dependencies": {
-        "@babel/runtime": "^7.7.2",
-        "@jimp/core": "^0.16.2"
+        "@jimp/core": "^0.22.4"
       }
     },
     "node_modules/@jimp/gif": {
-      "version": "0.16.2",
-      "resolved": "https://registry.npmjs.org/@jimp/gif/-/gif-0.16.2.tgz",
-      "integrity": "sha512-TMdyT9Q0paIKNtT7c5KzQD29CNCsI/t8ka28jMrBjEK7j5RRTvBfuoOnHv7pDJRCjCIqeUoaUSJ7QcciKic6CA==",
+      "version": "0.22.4",
+      "resolved": "https://registry.npmjs.org/@jimp/gif/-/gif-0.22.4.tgz",
+      "integrity": "sha512-KmN7GoaQTzLAX4JXLBRkIiZAXthgQdKe+Y7BOw4n6CMe6LAS/XCQqrYCG3Av/GqIO7UAKems6D7kIGAUuhpNlQ==",
       "dev": true,
       "dependencies": {
-        "@babel/runtime": "^7.7.2",
-        "@jimp/utils": "^0.16.2",
+        "@jimp/utils": "^0.22.4",
         "gifwrap": "^0.9.2",
         "omggif": "^1.0.9"
       },
@@ -464,80 +466,74 @@
       }
     },
     "node_modules/@jimp/jpeg": {
-      "version": "0.16.2",
-      "resolved": "https://registry.npmjs.org/@jimp/jpeg/-/jpeg-0.16.2.tgz",
-      "integrity": "sha512-BW5gZydgq6wdIwHd+3iUNgrTklvoQc/FUKSj9meM6A0FU21lUaansRX5BDdJqHkyXJLnnlDGwDt27J+hQuBAVw==",
+      "version": "0.22.4",
+      "resolved": "https://registry.npmjs.org/@jimp/jpeg/-/jpeg-0.22.4.tgz",
+      "integrity": "sha512-mMJNhEtJpne65mxpIXEvT0VIzmsKiZWmaFT/c2eQ2tBLEtWAFpkvoP+F7jEaU+F3Ur4fXKFkJ/xOSXtRr/gGNw==",
       "dev": true,
       "dependencies": {
-        "@babel/runtime": "^7.7.2",
-        "@jimp/utils": "^0.16.2",
-        "jpeg-js": "^0.4.2"
+        "@jimp/utils": "^0.22.4",
+        "jpeg-js": "^0.4.4"
       },
       "peerDependencies": {
         "@jimp/custom": ">=0.3.5"
       }
     },
     "node_modules/@jimp/plugin-blit": {
-      "version": "0.16.2",
-      "resolved": "https://registry.npmjs.org/@jimp/plugin-blit/-/plugin-blit-0.16.2.tgz",
-      "integrity": "sha512-Z31rRfV80gC/r+B/bOPSVVpJEWXUV248j7MdnMOFLu4vr8DMqXVo9jYqvwU/s4LSTMAMXqm4Jg6E/jQfadPKAg==",
+      "version": "0.22.4",
+      "resolved": "https://registry.npmjs.org/@jimp/plugin-blit/-/plugin-blit-0.22.4.tgz",
+      "integrity": "sha512-QQHe+rFarsxJQxWKlyHEMfLyXmUG9AiQky+8WfwjZVBYilIFyiBywOc3sThonOsru+7LOSUDmbN6mvbFk4R+gw==",
       "dev": true,
       "dependencies": {
-        "@babel/runtime": "^7.7.2",
-        "@jimp/utils": "^0.16.2"
+        "@jimp/utils": "^0.22.4"
       },
       "peerDependencies": {
         "@jimp/custom": ">=0.3.5"
       }
     },
     "node_modules/@jimp/plugin-blur": {
-      "version": "0.16.2",
-      "resolved": "https://registry.npmjs.org/@jimp/plugin-blur/-/plugin-blur-0.16.2.tgz",
-      "integrity": "sha512-ShkJCAzRI+1fAKPuLLgEkixpSpVmKTYaKEFROUcgmrv9AansDXGNCupchqVMTdxf8zPyW8rR1ilvG3OJobufLQ==",
+      "version": "0.22.4",
+      "resolved": "https://registry.npmjs.org/@jimp/plugin-blur/-/plugin-blur-0.22.4.tgz",
+      "integrity": "sha512-p57Ac5LEQckIogiwf7qyOojGvLOD08eMaQd5ylOhet/fbdwAzD/8flWFhSIKsdAVzvnfGcszuLtrsV07jDutTw==",
       "dev": true,
       "dependencies": {
-        "@babel/runtime": "^7.7.2",
-        "@jimp/utils": "^0.16.2"
+        "@jimp/utils": "^0.22.4"
       },
       "peerDependencies": {
         "@jimp/custom": ">=0.3.5"
       }
     },
     "node_modules/@jimp/plugin-circle": {
-      "version": "0.16.2",
-      "resolved": "https://registry.npmjs.org/@jimp/plugin-circle/-/plugin-circle-0.16.2.tgz",
-      "integrity": "sha512-6T4z/48F4Z5+YwAVCLOvXQcyGmo0E3WztxCz6XGQf66r4JJK78+zcCDYZFLMx0BGM0091FogNK4QniP8JaOkrA==",
+      "version": "0.22.4",
+      "resolved": "https://registry.npmjs.org/@jimp/plugin-circle/-/plugin-circle-0.22.4.tgz",
+      "integrity": "sha512-T+TpG+s+wM9kKHlpIEfCAfOM+QrYVqcMoWjkULddc0KtaDEhqgGYFhN+/SlzJfDbZKw0xUgIuAw89sXuzMIUjw==",
       "dev": true,
       "dependencies": {
-        "@babel/runtime": "^7.7.2",
-        "@jimp/utils": "^0.16.2"
+        "@jimp/utils": "^0.22.4"
       },
       "peerDependencies": {
         "@jimp/custom": ">=0.3.5"
       }
     },
     "node_modules/@jimp/plugin-color": {
-      "version": "0.16.2",
-      "resolved": "https://registry.npmjs.org/@jimp/plugin-color/-/plugin-color-0.16.2.tgz",
-      "integrity": "sha512-6oBV0g0J17/7E+aTquvUsgSc85nUbUi+64tIK5eFIDzvjhlqhjGNJYlc46KJMCWIs61qRJayQoZdL/iT/iQuGQ==",
+      "version": "0.22.4",
+      "resolved": "https://registry.npmjs.org/@jimp/plugin-color/-/plugin-color-0.22.4.tgz",
+      "integrity": "sha512-TZqcqepoCcIlF7VodPPfS3WET+LL5Y/XnXOBk4tWnG5i+lhNrs7/U0HOJY6Iw9o4g267DddnlfKWmunvzBcvOQ==",
       "dev": true,
       "dependencies": {
-        "@babel/runtime": "^7.7.2",
-        "@jimp/utils": "^0.16.2",
-        "tinycolor2": "^1.4.1"
+        "@jimp/utils": "^0.22.4",
+        "tinycolor2": "^1.6.0"
       },
       "peerDependencies": {
         "@jimp/custom": ">=0.3.5"
       }
     },
     "node_modules/@jimp/plugin-contain": {
-      "version": "0.16.2",
-      "resolved": "https://registry.npmjs.org/@jimp/plugin-contain/-/plugin-contain-0.16.2.tgz",
-      "integrity": "sha512-pLcxO3hVN3LCEhMNvpZ9B7xILHVlS433Vv16zFFJxLRqZdYvPLsc+ZzJhjAiHHuEjVblQrktHE3LGeQwGJPo0w==",
+      "version": "0.22.4",
+      "resolved": "https://registry.npmjs.org/@jimp/plugin-contain/-/plugin-contain-0.22.4.tgz",
+      "integrity": "sha512-Hl+TO4v+EpRfEl3R6k/bEgOGOpm6JqNfEIyCFWLi6yqJQjMGzBQ0vt+VHe2u3WIFaFrDWsGxeuFZBDzgtjTwxw==",
       "dev": true,
       "dependencies": {
-        "@babel/runtime": "^7.7.2",
-        "@jimp/utils": "^0.16.2"
+        "@jimp/utils": "^0.22.4"
       },
       "peerDependencies": {
         "@jimp/custom": ">=0.3.5",
@@ -547,13 +543,12 @@
       }
     },
     "node_modules/@jimp/plugin-cover": {
-      "version": "0.16.2",
-      "resolved": "https://registry.npmjs.org/@jimp/plugin-cover/-/plugin-cover-0.16.2.tgz",
-      "integrity": "sha512-gzWM7VvYeI8msyiwbUZxH+sGQEgO6Vd6adGxZ0CeKX00uQOe5lDzxb1Wjx7sHcJGz8a/5fmAuwz7rdDtpDUbkw==",
+      "version": "0.22.4",
+      "resolved": "https://registry.npmjs.org/@jimp/plugin-cover/-/plugin-cover-0.22.4.tgz",
+      "integrity": "sha512-KMTQjN/B7r/RNzoLFwnhqhLrgT0kMqTkBMEZQSopj5vPLPNjIX0ElEYC8AIVFKeZAV+1mYkyss+IDdxq4fyRng==",
       "dev": true,
       "dependencies": {
-        "@babel/runtime": "^7.7.2",
-        "@jimp/utils": "^0.16.2"
+        "@jimp/utils": "^0.22.4"
       },
       "peerDependencies": {
         "@jimp/custom": ">=0.3.5",
@@ -563,65 +558,60 @@
       }
     },
     "node_modules/@jimp/plugin-crop": {
-      "version": "0.16.2",
-      "resolved": "https://registry.npmjs.org/@jimp/plugin-crop/-/plugin-crop-0.16.2.tgz",
-      "integrity": "sha512-qCd3hfMEE+Z2EuuyXewgXRTtKJGIerWzc1zLEJztsUkPz5i73IGgkOL+mrNutZwGaXZbm+8SwUaGb46sxAO6Tw==",
+      "version": "0.22.4",
+      "resolved": "https://registry.npmjs.org/@jimp/plugin-crop/-/plugin-crop-0.22.4.tgz",
+      "integrity": "sha512-8krDt7xzBa1fbtlYCzEMZIgNjTkhgywho0FJpgIMkIUMjaZITS1Ea/Veb3UrWt8EsgQS6hxjGVE/Q1FvP5iPLA==",
       "dev": true,
       "dependencies": {
-        "@babel/runtime": "^7.7.2",
-        "@jimp/utils": "^0.16.2"
+        "@jimp/utils": "^0.22.4"
       },
       "peerDependencies": {
         "@jimp/custom": ">=0.3.5"
       }
     },
     "node_modules/@jimp/plugin-displace": {
-      "version": "0.16.2",
-      "resolved": "https://registry.npmjs.org/@jimp/plugin-displace/-/plugin-displace-0.16.2.tgz",
-      "integrity": "sha512-6nXdvNNjCdD95v2o3/jPeur903dz08lG4Y8gmr5oL2yVv9LSSbMonoXYrR/ASesdyXqGdXJLU4NL+yZs4zUqbQ==",
+      "version": "0.22.4",
+      "resolved": "https://registry.npmjs.org/@jimp/plugin-displace/-/plugin-displace-0.22.4.tgz",
+      "integrity": "sha512-3gBfwYVFrOjp8SUpb7H0UMgqvsG/sxY1PVBIfRW9MUCosiH1eE/Mo5cbxhQ6/w5f3sh23lBmG8W0WuSrnXLorg==",
       "dev": true,
       "dependencies": {
-        "@babel/runtime": "^7.7.2",
-        "@jimp/utils": "^0.16.2"
+        "@jimp/utils": "^0.22.4"
       },
       "peerDependencies": {
         "@jimp/custom": ">=0.3.5"
       }
     },
     "node_modules/@jimp/plugin-dither": {
-      "version": "0.16.2",
-      "resolved": "https://registry.npmjs.org/@jimp/plugin-dither/-/plugin-dither-0.16.2.tgz",
-      "integrity": "sha512-DERpIzy21ZanMkVsD0Tdy8HQLbD1E41OuvIzaMRoW4183PA6AgGNlrQoFTyXmzjy6FTy1SxaQgTEdouInAWZ9Q==",
+      "version": "0.22.4",
+      "resolved": "https://registry.npmjs.org/@jimp/plugin-dither/-/plugin-dither-0.22.4.tgz",
+      "integrity": "sha512-oOhdZBDJpSGIoTUhPOIvLIVUwILRWgrWdA4Vbzcyz2RHvaPHS8gdBH0EdIPbJ5agNyFnY8sJWFM7YKx/rLNKsw==",
       "dev": true,
       "dependencies": {
-        "@babel/runtime": "^7.7.2",
-        "@jimp/utils": "^0.16.2"
+        "@jimp/utils": "^0.22.4"
       },
       "peerDependencies": {
         "@jimp/custom": ">=0.3.5"
       }
     },
     "node_modules/@jimp/plugin-fisheye": {
-      "version": "0.16.2",
-      "resolved": "https://registry.npmjs.org/@jimp/plugin-fisheye/-/plugin-fisheye-0.16.2.tgz",
-      "integrity": "sha512-Df7PsGIwiIpQu3EygYCnaJyTfOwvwtYV3cmYJS7yFLtdiFUuod+hlSo5GkwEPLAy+QBxhUbDuUqnsWo4NQtbiQ==",
+      "version": "0.22.4",
+      "resolved": "https://registry.npmjs.org/@jimp/plugin-fisheye/-/plugin-fisheye-0.22.4.tgz",
+      "integrity": "sha512-2myNZyDrwUOV8MKd4NeULnEOojYF7XRbnRHiUPsNptpmK6g/gI4xt+5k7BallAYZD8ZLfZVjstUogsObprHd/Q==",
       "dev": true,
       "dependencies": {
-        "@babel/runtime": "^7.7.2",
-        "@jimp/utils": "^0.16.2"
+        "@jimp/utils": "^0.22.4"
       },
       "peerDependencies": {
         "@jimp/custom": ">=0.3.5"
       }
     },
     "node_modules/@jimp/plugin-flip": {
-      "version": "0.16.2",
-      "resolved": "https://registry.npmjs.org/@jimp/plugin-flip/-/plugin-flip-0.16.2.tgz",
-      "integrity": "sha512-+2uC8ioVQUr06mnjSWraskz2L33nJHze35LkQ8ZNsIpoZLkgvfiWatqAs5bj+1jGI/9kxoCFAaT1Is0f+a4/rw==",
+      "version": "0.22.4",
+      "resolved": "https://registry.npmjs.org/@jimp/plugin-flip/-/plugin-flip-0.22.4.tgz",
+      "integrity": "sha512-9FZ0k5N5leLDefeDjizXXTl17dzo23PYtCD/T4xeSVr96d1pQDwbeIk7pEhhHr1rl98tJe0U/OV2dFXFYauKPw==",
       "dev": true,
       "dependencies": {
-        "@babel/runtime": "^7.7.2",
-        "@jimp/utils": "^0.16.2"
+        "@jimp/utils": "^0.22.4"
       },
       "peerDependencies": {
         "@jimp/custom": ">=0.3.5",
@@ -629,66 +619,61 @@
       }
     },
     "node_modules/@jimp/plugin-gaussian": {
-      "version": "0.16.2",
-      "resolved": "https://registry.npmjs.org/@jimp/plugin-gaussian/-/plugin-gaussian-0.16.2.tgz",
-      "integrity": "sha512-2mnuDSg4ZEH8zcJig7DZZf4st/cYmQ5UYJKP76iGhZ+6JDACk6uejwAgT5xHecNhkVAaXMdCybA2eknH/9OE1w==",
+      "version": "0.22.4",
+      "resolved": "https://registry.npmjs.org/@jimp/plugin-gaussian/-/plugin-gaussian-0.22.4.tgz",
+      "integrity": "sha512-irOSwLdZ9kTq5Wd5dpkMgIMJVwemYcqgnzd04+P6TJGYmem2HR6JUCDpjbET3Fpbo/snFLm4mZ+2A+SmeCGjKA==",
       "dev": true,
       "dependencies": {
-        "@babel/runtime": "^7.7.2",
-        "@jimp/utils": "^0.16.2"
+        "@jimp/utils": "^0.22.4"
       },
       "peerDependencies": {
         "@jimp/custom": ">=0.3.5"
       }
     },
     "node_modules/@jimp/plugin-invert": {
-      "version": "0.16.2",
-      "resolved": "https://registry.npmjs.org/@jimp/plugin-invert/-/plugin-invert-0.16.2.tgz",
-      "integrity": "sha512-xFvHbVepTY/nus+6yXiYN1iq+UBRkT0MdnObbiQPstUrAsz0Imn6MWISsnAyMvcNxHGrxaxjuU777JT/esM0gg==",
+      "version": "0.22.4",
+      "resolved": "https://registry.npmjs.org/@jimp/plugin-invert/-/plugin-invert-0.22.4.tgz",
+      "integrity": "sha512-/WtZeLrF+H+mzbjqudeGvvSxudlHy1kyiP1gVWDxhYNQOuZJI57Vn20kSTYvHBNjvy31LV4/uestyX8j8tE2Qg==",
       "dev": true,
       "dependencies": {
-        "@babel/runtime": "^7.7.2",
-        "@jimp/utils": "^0.16.2"
+        "@jimp/utils": "^0.22.4"
       },
       "peerDependencies": {
         "@jimp/custom": ">=0.3.5"
       }
     },
     "node_modules/@jimp/plugin-mask": {
-      "version": "0.16.2",
-      "resolved": "https://registry.npmjs.org/@jimp/plugin-mask/-/plugin-mask-0.16.2.tgz",
-      "integrity": "sha512-AbdO85xxhfgEDdxYKpUotEI9ixiCMaIpfYHD5a5O/VWeimz2kuwhcrzlHGiyq1kKAgRcl0WEneTCZAHVSyvPKA==",
+      "version": "0.22.4",
+      "resolved": "https://registry.npmjs.org/@jimp/plugin-mask/-/plugin-mask-0.22.4.tgz",
+      "integrity": "sha512-U0SrOwBNKkMYTNPTz5CXeJdZ4c5easFlq2B9Txy0kPsav2OraTv8cZjpMxrWUejo/AQGVUDbaGtXMm9pE13/6w==",
       "dev": true,
       "dependencies": {
-        "@babel/runtime": "^7.7.2",
-        "@jimp/utils": "^0.16.2"
+        "@jimp/utils": "^0.22.4"
       },
       "peerDependencies": {
         "@jimp/custom": ">=0.3.5"
       }
     },
     "node_modules/@jimp/plugin-normalize": {
-      "version": "0.16.2",
-      "resolved": "https://registry.npmjs.org/@jimp/plugin-normalize/-/plugin-normalize-0.16.2.tgz",
-      "integrity": "sha512-+ItBWFwmB0Od7OfOtTYT1gm543PpHUgU8/DN55z83l1JqS0OomDJAe7BmCppo2405TN6YtVm/csXo7p4iWd/SQ==",
+      "version": "0.22.4",
+      "resolved": "https://registry.npmjs.org/@jimp/plugin-normalize/-/plugin-normalize-0.22.4.tgz",
+      "integrity": "sha512-XJiPBJGCHWmIzUdmL4mWP1Ev5LMp77oMmPXdgQGDty1cxfyo3CbkTjZSsnwF/XLlrQ1yfLW+8JB+ihGKcVEOxA==",
       "dev": true,
       "dependencies": {
-        "@babel/runtime": "^7.7.2",
-        "@jimp/utils": "^0.16.2"
+        "@jimp/utils": "^0.22.4"
       },
       "peerDependencies": {
         "@jimp/custom": ">=0.3.5"
       }
     },
     "node_modules/@jimp/plugin-print": {
-      "version": "0.16.2",
-      "resolved": "https://registry.npmjs.org/@jimp/plugin-print/-/plugin-print-0.16.2.tgz",
-      "integrity": "sha512-ifTGEeJ5UZTCiqC70HMeU3iXk/vsOmhWiwVGOXSFXhFeE8ZpDWvlmBsrMYnRrJGuaaogHOIrrQPI+kCdDBSBIQ==",
+      "version": "0.22.4",
+      "resolved": "https://registry.npmjs.org/@jimp/plugin-print/-/plugin-print-0.22.4.tgz",
+      "integrity": "sha512-mayiPhg6c7KYjvq3fYOW9ohhXD1eWdEiseV9dAWqTOEbDbohT8S6eTGhVIiVa2sVySLcpNEKZSk07c5EhJAMcw==",
       "dev": true,
       "dependencies": {
-        "@babel/runtime": "^7.7.2",
-        "@jimp/utils": "^0.16.2",
-        "load-bmfont": "^1.4.0"
+        "@jimp/utils": "^0.22.4",
+        "load-bmfont": "^1.4.1"
       },
       "peerDependencies": {
         "@jimp/custom": ">=0.3.5",
@@ -696,26 +681,24 @@
       }
     },
     "node_modules/@jimp/plugin-resize": {
-      "version": "0.16.2",
-      "resolved": "https://registry.npmjs.org/@jimp/plugin-resize/-/plugin-resize-0.16.2.tgz",
-      "integrity": "sha512-gE4N9l6xuwzacFZ2EPCGZCJ/xR+aX2V7GdMndIl/6kYIw5/eib1SFuF9AZLvIPSFuE1FnGo8+vT0pr++SSbhYg==",
+      "version": "0.22.4",
+      "resolved": "https://registry.npmjs.org/@jimp/plugin-resize/-/plugin-resize-0.22.4.tgz",
+      "integrity": "sha512-2wMdpPNGf6Zo2lfJg1QHHQ+ds5baQH75IcFpdjw717dcEISpn1jPG//iClXOGh16OJsRQlwHESaZTgEo/5Dw/g==",
       "dev": true,
       "dependencies": {
-        "@babel/runtime": "^7.7.2",
-        "@jimp/utils": "^0.16.2"
+        "@jimp/utils": "^0.22.4"
       },
       "peerDependencies": {
         "@jimp/custom": ">=0.3.5"
       }
     },
     "node_modules/@jimp/plugin-rotate": {
-      "version": "0.16.2",
-      "resolved": "https://registry.npmjs.org/@jimp/plugin-rotate/-/plugin-rotate-0.16.2.tgz",
-      "integrity": "sha512-/CTEYkR1HrgmnE0VqPhhbBARbDAfFX590LWGIpxcYIYsUUGQCadl+8Qo4UX13FH0Nt8UHEtPA+O2x08uPYg9UA==",
+      "version": "0.22.4",
+      "resolved": "https://registry.npmjs.org/@jimp/plugin-rotate/-/plugin-rotate-0.22.4.tgz",
+      "integrity": "sha512-g08LBsPENbeA6NVoeq0iuDgAL89+N+aZrvYVKYkiJZIM7vUvueJyAIq4+bjDl4r54OR8XBFX0GsrKsqrULh1eA==",
       "dev": true,
       "dependencies": {
-        "@babel/runtime": "^7.7.2",
-        "@jimp/utils": "^0.16.2"
+        "@jimp/utils": "^0.22.4"
       },
       "peerDependencies": {
         "@jimp/custom": ">=0.3.5",
@@ -725,13 +708,12 @@
       }
     },
     "node_modules/@jimp/plugin-scale": {
-      "version": "0.16.2",
-      "resolved": "https://registry.npmjs.org/@jimp/plugin-scale/-/plugin-scale-0.16.2.tgz",
-      "integrity": "sha512-3inuxfrlquyLaqFdiiiQNJUurR0WbvN5wAf1qcYX2LubG1AG8grayYD6H7XVoxfUGTZXh1kpmeirEYlqA2zxcw==",
+      "version": "0.22.4",
+      "resolved": "https://registry.npmjs.org/@jimp/plugin-scale/-/plugin-scale-0.22.4.tgz",
+      "integrity": "sha512-cJiLQtTcNk6/+j05R23TFGXy+smDV0BdlmzJVDb+7Ye9qcmWpkdjVSioQQqZr0QScIYKhhRCY/lFTepBx67yzw==",
       "dev": true,
       "dependencies": {
-        "@babel/runtime": "^7.7.2",
-        "@jimp/utils": "^0.16.2"
+        "@jimp/utils": "^0.22.4"
       },
       "peerDependencies": {
         "@jimp/custom": ">=0.3.5",
@@ -739,13 +721,12 @@
       }
     },
     "node_modules/@jimp/plugin-shadow": {
-      "version": "0.16.2",
-      "resolved": "https://registry.npmjs.org/@jimp/plugin-shadow/-/plugin-shadow-0.16.2.tgz",
-      "integrity": "sha512-Q0aIs2/L6fWMcEh9Ms73u34bT1hyUMw/oxaVoIzOLo6/E8YzCs2Bi63H0/qaPS0MQpEppI++kvosPbblABY79w==",
+      "version": "0.22.4",
+      "resolved": "https://registry.npmjs.org/@jimp/plugin-shadow/-/plugin-shadow-0.22.4.tgz",
+      "integrity": "sha512-a5hdpzGBzBo91DNiKaGvs8iJbs2bYQmDRm/BrCh4NET+h5l5AwXNu/Ak0bWRhN16YQ55XYNGHer2jOwBPrf2WQ==",
       "dev": true,
       "dependencies": {
-        "@babel/runtime": "^7.7.2",
-        "@jimp/utils": "^0.16.2"
+        "@jimp/utils": "^0.22.4"
       },
       "peerDependencies": {
         "@jimp/custom": ">=0.3.5",
@@ -754,13 +735,12 @@
       }
     },
     "node_modules/@jimp/plugin-threshold": {
-      "version": "0.16.2",
-      "resolved": "https://registry.npmjs.org/@jimp/plugin-threshold/-/plugin-threshold-0.16.2.tgz",
-      "integrity": "sha512-gyOwmBgjtMPvcuyOhkP6dOGWbQdaTfhcBRN22mYeI/k/Wh/Zh1OI21F6eKLApsVRmg15MoFnkrCz64RROC34sw==",
+      "version": "0.22.4",
+      "resolved": "https://registry.npmjs.org/@jimp/plugin-threshold/-/plugin-threshold-0.22.4.tgz",
+      "integrity": "sha512-jTT/+p2zb2NESzd7O0bVRowiQszoaHeBf2OgP7lFde10fHd+fn78m5brUmSmlGAdlMRwm8S8ZcxTj5ZjdQns5w==",
       "dev": true,
       "dependencies": {
-        "@babel/runtime": "^7.7.2",
-        "@jimp/utils": "^0.16.2"
+        "@jimp/utils": "^0.22.4"
       },
       "peerDependencies": {
         "@jimp/custom": ">=0.3.5",
@@ -769,33 +749,32 @@
       }
     },
     "node_modules/@jimp/plugins": {
-      "version": "0.16.2",
-      "resolved": "https://registry.npmjs.org/@jimp/plugins/-/plugins-0.16.2.tgz",
-      "integrity": "sha512-zCvYtCgctmC0tkYEu+y+kSwSIZBsNznqJ3/3vkpzxdyjd6wCfNY5Qc/68MPrLc1lmdeGo4cOOTYHG7Vc6myzRw==",
-      "dev": true,
-      "dependencies": {
-        "@babel/runtime": "^7.7.2",
-        "@jimp/plugin-blit": "^0.16.2",
-        "@jimp/plugin-blur": "^0.16.2",
-        "@jimp/plugin-circle": "^0.16.2",
-        "@jimp/plugin-color": "^0.16.2",
-        "@jimp/plugin-contain": "^0.16.2",
-        "@jimp/plugin-cover": "^0.16.2",
-        "@jimp/plugin-crop": "^0.16.2",
-        "@jimp/plugin-displace": "^0.16.2",
-        "@jimp/plugin-dither": "^0.16.2",
-        "@jimp/plugin-fisheye": "^0.16.2",
-        "@jimp/plugin-flip": "^0.16.2",
-        "@jimp/plugin-gaussian": "^0.16.2",
-        "@jimp/plugin-invert": "^0.16.2",
-        "@jimp/plugin-mask": "^0.16.2",
-        "@jimp/plugin-normalize": "^0.16.2",
-        "@jimp/plugin-print": "^0.16.2",
-        "@jimp/plugin-resize": "^0.16.2",
-        "@jimp/plugin-rotate": "^0.16.2",
-        "@jimp/plugin-scale": "^0.16.2",
-        "@jimp/plugin-shadow": "^0.16.2",
-        "@jimp/plugin-threshold": "^0.16.2",
+      "version": "0.22.4",
+      "resolved": "https://registry.npmjs.org/@jimp/plugins/-/plugins-0.22.4.tgz",
+      "integrity": "sha512-yAxcA4UR3Bs7j73I7wt4ty52vm5MzPmr+8DYk8jrS/ng2Z2iuXzbcTe4mf9eEqXYVah3rTIggo4dPjW75DRZtA==",
+      "dev": true,
+      "dependencies": {
+        "@jimp/plugin-blit": "^0.22.4",
+        "@jimp/plugin-blur": "^0.22.4",
+        "@jimp/plugin-circle": "^0.22.4",
+        "@jimp/plugin-color": "^0.22.4",
+        "@jimp/plugin-contain": "^0.22.4",
+        "@jimp/plugin-cover": "^0.22.4",
+        "@jimp/plugin-crop": "^0.22.4",
+        "@jimp/plugin-displace": "^0.22.4",
+        "@jimp/plugin-dither": "^0.22.4",
+        "@jimp/plugin-fisheye": "^0.22.4",
+        "@jimp/plugin-flip": "^0.22.4",
+        "@jimp/plugin-gaussian": "^0.22.4",
+        "@jimp/plugin-invert": "^0.22.4",
+        "@jimp/plugin-mask": "^0.22.4",
+        "@jimp/plugin-normalize": "^0.22.4",
+        "@jimp/plugin-print": "^0.22.4",
+        "@jimp/plugin-resize": "^0.22.4",
+        "@jimp/plugin-rotate": "^0.22.4",
+        "@jimp/plugin-scale": "^0.22.4",
+        "@jimp/plugin-shadow": "^0.22.4",
+        "@jimp/plugin-threshold": "^0.22.4",
         "timm": "^1.6.1"
       },
       "peerDependencies": {
@@ -803,44 +782,41 @@
       }
     },
     "node_modules/@jimp/png": {
-      "version": "0.16.2",
-      "resolved": "https://registry.npmjs.org/@jimp/png/-/png-0.16.2.tgz",
-      "integrity": "sha512-sFOtOSz/tzDwXEChFQ/Nxe+0+vG3Tj0eUxnZVDUG/StXE9dI8Bqmwj3MIa0EgK5s+QG3YlnDOmlPUa4JqmeYeQ==",
+      "version": "0.22.4",
+      "resolved": "https://registry.npmjs.org/@jimp/png/-/png-0.22.4.tgz",
+      "integrity": "sha512-kDovx9dTyV/TSR40HQHdRyVgNNb7Cny4/0PPEa+xeR7snuDC3dV5hu9s/QJwY0RMGiAkiuKDpiaBuSZuz8dwRQ==",
       "dev": true,
       "dependencies": {
-        "@babel/runtime": "^7.7.2",
-        "@jimp/utils": "^0.16.2",
-        "pngjs": "^3.3.3"
+        "@jimp/utils": "^0.22.4",
+        "pngjs": "^6.0.0"
       },
       "peerDependencies": {
         "@jimp/custom": ">=0.3.5"
       }
     },
     "node_modules/@jimp/tiff": {
-      "version": "0.16.2",
-      "resolved": "https://registry.npmjs.org/@jimp/tiff/-/tiff-0.16.2.tgz",
-      "integrity": "sha512-ADcdqmtZF+U2YoaaHTzFX8D6NFpmN4WZUT0BPMerEuY7Cq8QoLYU22z2h034FrVW+Rbi1b3y04sB9iDiQAlf2w==",
+      "version": "0.22.4",
+      "resolved": "https://registry.npmjs.org/@jimp/tiff/-/tiff-0.22.4.tgz",
+      "integrity": "sha512-RStUATRnb+unYzzuGxU+SPZALqh5NxYdcS6UGTBvhCMlijopGiY/iL01wstIOst0ypKIjwbcSVj7mAHn6B7Qbw==",
       "dev": true,
       "dependencies": {
-        "@babel/runtime": "^7.7.2",
-        "utif": "^2.0.1"
+        "utif2": "^4.0.1"
       },
       "peerDependencies": {
         "@jimp/custom": ">=0.3.5"
       }
     },
     "node_modules/@jimp/types": {
-      "version": "0.16.2",
-      "resolved": "https://registry.npmjs.org/@jimp/types/-/types-0.16.2.tgz",
-      "integrity": "sha512-0Ue5Sq0XnDF6TirisWv5E+8uOnRcd8vRLuwocJOhF76NIlcQrz+5r2k2XWKcr3d+11n28dHLXW5TKSqrUopxhA==",
+      "version": "0.22.4",
+      "resolved": "https://registry.npmjs.org/@jimp/types/-/types-0.22.4.tgz",
+      "integrity": "sha512-v3hm8LGc3we6P6ML0ticiLX7wtdvywrKthYxqVrJVIu3vRL0Z4q3ngFjwzqDmaIF8wC0neq98s/t7ODWfeIiRQ==",
       "dev": true,
       "dependencies": {
-        "@babel/runtime": "^7.7.2",
-        "@jimp/bmp": "^0.16.2",
-        "@jimp/gif": "^0.16.2",
-        "@jimp/jpeg": "^0.16.2",
-        "@jimp/png": "^0.16.2",
-        "@jimp/tiff": "^0.16.2",
+        "@jimp/bmp": "^0.22.4",
+        "@jimp/gif": "^0.22.4",
+        "@jimp/jpeg": "^0.22.4",
+        "@jimp/png": "^0.22.4",
+        "@jimp/tiff": "^0.22.4",
         "timm": "^1.6.1"
       },
       "peerDependencies": {
@@ -848,12 +824,11 @@
       }
     },
     "node_modules/@jimp/utils": {
-      "version": "0.16.2",
-      "resolved": "https://registry.npmjs.org/@jimp/utils/-/utils-0.16.2.tgz",
-      "integrity": "sha512-XENrPvmigiXZQ8E2nxJqO6UVvWBLzbNwyYi3Y8Q1IECoYhYI3kgOQ0fmy4G269Vz1V0omh1bNmC42r4OfXg1Jg==",
+      "version": "0.22.4",
+      "resolved": "https://registry.npmjs.org/@jimp/utils/-/utils-0.22.4.tgz",
+      "integrity": "sha512-EPaBMNg4NvVXnMpSFJEsdCQqdSVU2ACreAL+Ipkq19C/FkDEj9Q10t6Mjx8zOe/AAjBQj1vTALS/DykcHOn4bQ==",
       "dev": true,
       "dependencies": {
-        "@babel/runtime": "^7.7.2",
         "regenerator-runtime": "^0.13.3"
       }
     },
@@ -1224,9 +1199,9 @@
       }
     },
     "node_modules/@supabase/supabase-js": {
-      "version": "2.7.0",
-      "resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.7.0.tgz",
-      "integrity": "sha512-0Ry6rcxeya0VRbPh6fHfgPcmH7X9gMILon7/PWoVJSjYsQntv7EGUNNeHtLutBlm8St74s5Q4sjsqJOPslDG4Q==",
+      "version": "2.7.1",
+      "resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.7.1.tgz",
+      "integrity": "sha512-Q/e+JAluZEvy7D4ul3aAs3aOiKkGvHlZULy6wjchWQyU9YlJKZLr6VPYcwUeitcnRKZi4al5iTS55LgdJFfqIA==",
       "dependencies": {
         "@supabase/functions-js": "^2.0.0",
         "@supabase/gotrue-js": "^2.10.2",
@@ -1237,9 +1212,9 @@
       }
     },
     "node_modules/@sveltejs/adapter-auto": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/@sveltejs/adapter-auto/-/adapter-auto-1.0.2.tgz",
-      "integrity": "sha512-UXpEO/gutERZnD+Z5Vi4J/ifD3WSRuCI7xwtLJTcKNQvJ6t5Xsj1X3Mw2F8Vv/XTUuxf7xPLYUgThU331r0Y9w==",
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/@sveltejs/adapter-auto/-/adapter-auto-2.0.0.tgz",
+      "integrity": "sha512-b+gkHFZgD771kgV3aO4avHFd7y1zhmMYy9i6xOK7m/rwmwaRO8gnF5zBc0Rgca80B2PMU1bKNxyBTHA14OzUAQ==",
       "dev": true,
       "dependencies": {
         "import-meta-resolve": "^2.2.0"
@@ -1249,9 +1224,9 @@
       }
     },
     "node_modules/@sveltejs/kit": {
-      "version": "1.3.10",
-      "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-1.3.10.tgz",
-      "integrity": "sha512-I3DgWCwTYbTz4ZPCJIRkSDrKkMu0bsdk6ghqsOBVNqesf1wBdTdfkXhag3ESWgIEjUV3VUIWPQF7fnt7328mhQ==",
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-1.5.0.tgz",
+      "integrity": "sha512-AkWgCO9i2djZjTqCgIQJ5XfnSzRINowh2w2Gk9wDRuTwxKizSuYe3jNvds/HCDDGHo8XE5E0yWNC9j2XxbrX+g==",
       "dev": true,
       "hasInstallScript": true,
       "dependencies": {
@@ -1267,7 +1242,7 @@
         "set-cookie-parser": "^2.5.1",
         "sirv": "^2.0.2",
         "tiny-glob": "^0.2.9",
-        "undici": "5.16.0"
+        "undici": "5.18.0"
       },
       "bin": {
         "svelte-kit": "svelte-kit.js"
@@ -1303,9 +1278,9 @@
       }
     },
     "node_modules/@sveltejs/site-kit": {
-      "version": "3.2.1",
-      "resolved": "https://registry.npmjs.org/@sveltejs/site-kit/-/site-kit-3.2.1.tgz",
-      "integrity": "sha512-TrWTMI/7JVHHKWR1Eb2H77FfyHSw9HjlYjSyK1Hk8ZTJe4aQsO0fr/J+bfGB+Iz33RU0PyerA60KHv47KdISUw==",
+      "version": "3.2.2",
+      "resolved": "https://registry.npmjs.org/@sveltejs/site-kit/-/site-kit-3.2.2.tgz",
+      "integrity": "sha512-HBLtfNdLr5Ykl8i8CvJcYjib7zMIJupg4T/omplp3ccpgpiUh26tk71vRG1+a6yMkfbfy0ShoPb9uwNril5cnw==",
       "dev": true
     },
     "node_modules/@sveltejs/vite-plugin-svelte": {
@@ -1329,6 +1304,12 @@
         "vite": "^4.0.0"
       }
     },
+    "node_modules/@tokenizer/token": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz",
+      "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==",
+      "dev": true
+    },
     "node_modules/@types/cookie": {
       "version": "0.5.1",
       "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.5.1.tgz",
@@ -1340,6 +1321,12 @@
       "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.0.tgz",
       "integrity": "sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ=="
     },
+    "node_modules/@types/marked": {
+      "version": "4.0.8",
+      "resolved": "https://registry.npmjs.org/@types/marked/-/marked-4.0.8.tgz",
+      "integrity": "sha512-HVNzMT5QlWCOdeuBsgXP8EZzKUf0+AXzN+sLmjvaB3ZlLqO+e4u0uXrdw9ub69wBKFs+c6/pA4r9sy6cCDvImw==",
+      "dev": true
+    },
     "node_modules/@types/node": {
       "version": "16.9.1",
       "resolved": "https://registry.npmjs.org/@types/node/-/node-16.9.1.tgz",
@@ -1351,6 +1338,12 @@
       "resolved": "https://registry.npmjs.org/@types/phoenix/-/phoenix-1.5.4.tgz",
       "integrity": "sha512-L5eZmzw89eXBKkiqVBcJfU1QGx9y+wurRIEgt0cuLH0hwNtVUxtx+6cu0R2STwWj468sjXyBYPYDtGclUd1kjQ=="
     },
+    "node_modules/@types/prismjs": {
+      "version": "1.26.0",
+      "resolved": "https://registry.npmjs.org/@types/prismjs/-/prismjs-1.26.0.tgz",
+      "integrity": "sha512-ZTaqn/qSqUuAq1YwvOFQfVW1AR/oQJlLSZVustdjwI+GZ8kr0MSHBj0tsXPW1EqHubx50gtBEjbPGsdZwQwCjQ==",
+      "dev": true
+    },
     "node_modules/@types/pug": {
       "version": "2.0.6",
       "resolved": "https://registry.npmjs.org/@types/pug/-/pug-2.0.6.tgz",
@@ -1366,6 +1359,35 @@
         "@types/node": "*"
       }
     },
+    "node_modules/@typescript/twoslash": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/@typescript/twoslash/-/twoslash-3.1.0.tgz",
+      "integrity": "sha512-kTwMUQ8xtAZaC4wb2XuLkPqFVBj2dNBueMQ89NWEuw87k2nLBbuafeG5cob/QEr6YduxIdTVUjix0MtC7mPlmg==",
+      "dev": true,
+      "dependencies": {
+        "@typescript/vfs": "1.3.5",
+        "debug": "^4.1.1",
+        "lz-string": "^1.4.4"
+      }
+    },
+    "node_modules/@typescript/twoslash/node_modules/@typescript/vfs": {
+      "version": "1.3.5",
+      "resolved": "https://registry.npmjs.org/@typescript/vfs/-/vfs-1.3.5.tgz",
+      "integrity": "sha512-pI8Saqjupf9MfLw7w2+og+fmb0fZS0J6vsKXXrp4/PDXEFvntgzXmChCXC/KefZZS0YGS6AT8e0hGAJcTsdJlg==",
+      "dev": true,
+      "dependencies": {
+        "debug": "^4.1.1"
+      }
+    },
+    "node_modules/@typescript/vfs": {
+      "version": "1.3.4",
+      "resolved": "https://registry.npmjs.org/@typescript/vfs/-/vfs-1.3.4.tgz",
+      "integrity": "sha512-RbyJiaAGQPIcAGWFa3jAXSuAexU4BFiDRF1g3hy7LmRqfNpYlTQWGXjcrOaVZjJ8YkkpuwG0FcsYvtWQpd9igQ==",
+      "dev": true,
+      "dependencies": {
+        "debug": "^4.1.1"
+      }
+    },
     "node_modules/acorn": {
       "version": "8.8.2",
       "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz",
@@ -1377,6 +1399,12 @@
         "node": ">=0.4.0"
       }
     },
+    "node_modules/ansi-sequence-parser": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/ansi-sequence-parser/-/ansi-sequence-parser-1.1.0.tgz",
+      "integrity": "sha512-lEm8mt52to2fT8GhciPCGeCXACSz2UwIN4X2e2LJSnZ5uAbn2/dsYdOmUXq0AtWS5cpAupysIneExOgH0Vd2TQ==",
+      "dev": true
+    },
     "node_modules/any-base": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/any-base/-/any-base-1.1.0.tgz",
@@ -2017,12 +2045,20 @@
       "dev": true
     },
     "node_modules/file-type": {
-      "version": "9.0.0",
-      "resolved": "https://registry.npmjs.org/file-type/-/file-type-9.0.0.tgz",
-      "integrity": "sha512-Qe/5NJrgIOlwijpq3B7BEpzPFcgzggOTagZmkXQY4LA6bsXKTUstK7Wp12lEJ/mLKTpvIZxmIuRcLYWT6ov9lw==",
+      "version": "16.5.4",
+      "resolved": "https://registry.npmjs.org/file-type/-/file-type-16.5.4.tgz",
+      "integrity": "sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw==",
       "dev": true,
+      "dependencies": {
+        "readable-web-to-node-stream": "^3.0.0",
+        "strtok3": "^6.2.4",
+        "token-types": "^4.1.1"
+      },
       "engines": {
-        "node": ">=6"
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sindresorhus/file-type?sponsor=1"
       }
     },
     "node_modules/fill-range": {
@@ -2350,16 +2386,45 @@
       "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
       "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA=="
     },
+    "node_modules/isomorphic-fetch": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-3.0.0.tgz",
+      "integrity": "sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA==",
+      "dev": true,
+      "dependencies": {
+        "node-fetch": "^2.6.1",
+        "whatwg-fetch": "^3.4.1"
+      }
+    },
+    "node_modules/isomorphic-fetch/node_modules/node-fetch": {
+      "version": "2.6.9",
+      "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.9.tgz",
+      "integrity": "sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg==",
+      "dev": true,
+      "dependencies": {
+        "whatwg-url": "^5.0.0"
+      },
+      "engines": {
+        "node": "4.x || >=6.0.0"
+      },
+      "peerDependencies": {
+        "encoding": "^0.1.0"
+      },
+      "peerDependenciesMeta": {
+        "encoding": {
+          "optional": true
+        }
+      }
+    },
     "node_modules/jimp": {
-      "version": "0.16.2",
-      "resolved": "https://registry.npmjs.org/jimp/-/jimp-0.16.2.tgz",
-      "integrity": "sha512-UpItBk81a92f8oEyoGYbO3YK4QcM0hoIyuGHmShoF9Ov63P5Qo7Q/X2xsAgnODmSuDJFOtrPtJd5GSWW4LKdOQ==",
+      "version": "0.22.4",
+      "resolved": "https://registry.npmjs.org/jimp/-/jimp-0.22.4.tgz",
+      "integrity": "sha512-reGESbcYp38VlGtdAe8qrmbjLLEYXMrQWc2XXb7+czulKfCCidUHEpNfrS3hx5XXMWrAmoYKkxPTqCvll6Q6ug==",
       "dev": true,
       "dependencies": {
-        "@babel/runtime": "^7.7.2",
-        "@jimp/custom": "^0.16.2",
-        "@jimp/plugins": "^0.16.2",
-        "@jimp/types": "^0.16.2",
+        "@jimp/custom": "^0.22.4",
+        "@jimp/plugins": "^0.22.4",
+        "@jimp/types": "^0.22.4",
         "regenerator-runtime": "^0.13.3"
       }
     },
@@ -2369,6 +2434,12 @@
       "integrity": "sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg==",
       "dev": true
     },
+    "node_modules/jsonc-parser": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz",
+      "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==",
+      "dev": true
+    },
     "node_modules/kleur": {
       "version": "4.1.5",
       "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz",
@@ -2418,6 +2489,15 @@
         "node": ">=10"
       }
     },
+    "node_modules/lz-string": {
+      "version": "1.4.4",
+      "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.4.4.tgz",
+      "integrity": "sha512-0ckx7ZHRPqb0oUm8zNr+90mtf9DQB60H1wMCjBtfi62Kl3a7JbHob6gA2bC+xRvZoOL+1hzUK8jeuEIQE8svEQ==",
+      "dev": true,
+      "bin": {
+        "lz-string": "bin/bin.js"
+      }
+    },
     "node_modules/magic-string": {
       "version": "0.27.0",
       "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.27.0.tgz",
@@ -2742,6 +2822,19 @@
       "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
       "dev": true
     },
+    "node_modules/peek-readable": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-4.1.0.tgz",
+      "integrity": "sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/Borewit"
+      }
+    },
     "node_modules/phin": {
       "version": "2.9.3",
       "resolved": "https://registry.npmjs.org/phin/-/phin-2.9.3.tgz",
@@ -2778,7 +2871,7 @@
         "pixelmatch": "bin/pixelmatch"
       }
     },
-    "node_modules/pngjs": {
+    "node_modules/pixelmatch/node_modules/pngjs": {
       "version": "3.4.0",
       "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.4.0.tgz",
       "integrity": "sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==",
@@ -2787,6 +2880,15 @@
         "node": ">=4.0.0"
       }
     },
+    "node_modules/pngjs": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-6.0.0.tgz",
+      "integrity": "sha512-TRzzuFRRmEoSW/p1KVAmiOgPco2Irlah+bGFCeNfJXxxYGwSw7YwAOAcd7X28K/m5bjBWKsC29KyoMfHbypayg==",
+      "dev": true,
+      "engines": {
+        "node": ">=12.13.0"
+      }
+    },
     "node_modules/postcss": {
       "version": "8.4.21",
       "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz",
@@ -2951,6 +3053,22 @@
         "node": ">= 6"
       }
     },
+    "node_modules/readable-web-to-node-stream": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz",
+      "integrity": "sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==",
+      "dev": true,
+      "dependencies": {
+        "readable-stream": "^3.6.0"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/Borewit"
+      }
+    },
     "node_modules/readdirp": {
       "version": "3.6.0",
       "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
@@ -3111,9 +3229,9 @@
       }
     },
     "node_modules/satori": {
-      "version": "0.1.2",
-      "resolved": "https://registry.npmjs.org/satori/-/satori-0.1.2.tgz",
-      "integrity": "sha512-1P08+PyNUzJ2P0bdI9reSJw+B/Whv9nThvoX4HAiAp9lNNTHtwtN9525TuEtELml7AEEvECpeOvBqMkAxcuQeQ==",
+      "version": "0.2.3",
+      "resolved": "https://registry.npmjs.org/satori/-/satori-0.2.3.tgz",
+      "integrity": "sha512-jl/+sFkz3pBfGip7034Uvn/Hp1FZq4prKXIZO28rV90DS3jTU6uXBV0FRX4ooYoO27f3rGNXKhSKyDST5FdmQA==",
       "dev": true,
       "dependencies": {
         "@shuding/opentype.js": "1.4.0-beta.0",
@@ -3204,6 +3322,47 @@
         "node": ">=4"
       }
     },
+    "node_modules/shiki": {
+      "version": "0.14.0",
+      "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.14.0.tgz",
+      "integrity": "sha512-fb9Fg1Yx/ElVJcTqPQIEOSfn7mSZlrT1W3CkymY08lL2Jsi+t7jPcZzKO1lCsQwlSDuyNhHvolnyA2OI4EgJNg==",
+      "dev": true,
+      "dependencies": {
+        "ansi-sequence-parser": "^1.1.0",
+        "jsonc-parser": "^3.2.0",
+        "vscode-oniguruma": "^1.7.0",
+        "vscode-textmate": "^8.0.0"
+      }
+    },
+    "node_modules/shiki-twoslash": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/shiki-twoslash/-/shiki-twoslash-3.1.0.tgz",
+      "integrity": "sha512-uDqrTutOIZzyHbo103GsK7Vvc10saK1XCCivnOQ1NHJzgp3FBilEpftGeVzVSMOJs+JyhI7whkvhXV7kXQ5zCg==",
+      "dev": true,
+      "dependencies": {
+        "@typescript/twoslash": "3.1.0",
+        "@typescript/vfs": "1.3.4",
+        "shiki": "0.10.1",
+        "typescript": ">3"
+      }
+    },
+    "node_modules/shiki-twoslash/node_modules/shiki": {
+      "version": "0.10.1",
+      "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.10.1.tgz",
+      "integrity": "sha512-VsY7QJVzU51j5o1+DguUd+6vmCmZ5v/6gYu4vyYAhzjuNQU6P/vmSy4uQaOhvje031qQMiW0d2BwgMH52vqMng==",
+      "dev": true,
+      "dependencies": {
+        "jsonc-parser": "^3.0.0",
+        "vscode-oniguruma": "^1.6.1",
+        "vscode-textmate": "5.2.0"
+      }
+    },
+    "node_modules/shiki-twoslash/node_modules/vscode-textmate": {
+      "version": "5.2.0",
+      "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-5.2.0.tgz",
+      "integrity": "sha512-Uw5ooOQxRASHgu6C7GVvUxisKXfSgW4oFlO+aa+PAkgmH89O3CXxEEzNRNtHSqtXFTl0nAC1uYj0GMSH27uwtQ==",
+      "dev": true
+    },
     "node_modules/simple-concat": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz",
@@ -3347,6 +3506,23 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/strtok3": {
+      "version": "6.3.0",
+      "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-6.3.0.tgz",
+      "integrity": "sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw==",
+      "dev": true,
+      "dependencies": {
+        "@tokenizer/token": "^0.3.0",
+        "peek-readable": "^4.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/Borewit"
+      }
+    },
     "node_modules/supports-preserve-symlinks-flag": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
@@ -3515,9 +3691,9 @@
       }
     },
     "node_modules/tinycolor2": {
-      "version": "1.5.2",
-      "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.5.2.tgz",
-      "integrity": "sha512-h80m9GPFGbcLzZByXlNSEhp1gf8Dy+VX/2JCGUZsWLo7lV1mnE/XlxGYgRBoMLJh1lIDXP0EMC4RPTjlRaV+Bg==",
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.6.0.tgz",
+      "integrity": "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==",
       "dev": true
     },
     "node_modules/to-regex-range": {
@@ -3532,6 +3708,23 @@
         "node": ">=8.0"
       }
     },
+    "node_modules/token-types": {
+      "version": "4.2.1",
+      "resolved": "https://registry.npmjs.org/token-types/-/token-types-4.2.1.tgz",
+      "integrity": "sha512-6udB24Q737UD/SDsKAHI9FCRP7Bqc9D/MQUV02ORQg5iskjtLJlZJNdN4kKtcdtwCeWIwIHDGaUsTsCCAa8sFQ==",
+      "dev": true,
+      "dependencies": {
+        "@tokenizer/token": "^0.3.0",
+        "ieee754": "^1.2.1"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/Borewit"
+      }
+    },
     "node_modules/totalist": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.0.tgz",
@@ -3591,9 +3784,9 @@
       "dev": true
     },
     "node_modules/undici": {
-      "version": "5.16.0",
-      "resolved": "https://registry.npmjs.org/undici/-/undici-5.16.0.tgz",
-      "integrity": "sha512-KWBOXNv6VX+oJQhchXieUznEmnJMqgXMbs0xxH2t8q/FUAWSJvOSr/rMaZKnX5RIVq7JDn0JbP4BOnKG2SGXLQ==",
+      "version": "5.18.0",
+      "resolved": "https://registry.npmjs.org/undici/-/undici-5.18.0.tgz",
+      "integrity": "sha512-1iVwbhonhFytNdg0P4PqyIAXbdlVZVebtPDvuM36m66mRw4OGrCm2MYynJv/UENFLdP13J1nPVQzVE2zTs1OeA==",
       "dev": true,
       "dependencies": {
         "busboy": "^1.6.0"
@@ -3614,13 +3807,13 @@
         "node": ">=6.14.2"
       }
     },
-    "node_modules/utif": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/utif/-/utif-2.0.1.tgz",
-      "integrity": "sha512-Z/S1fNKCicQTf375lIP9G8Sa1H/phcysstNrrSdZKj1f9g58J4NMgb5IgiEZN9/nLMPDwF0W7hdOe9Qq2IYoLg==",
+    "node_modules/utif2": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/utif2/-/utif2-4.0.1.tgz",
+      "integrity": "sha512-KMaD76dbzK1VjbwsckHJiqDjhP3pbpwyV+FdqkY6XFQenc2o/HS6pjPSYdu4+NQMHf2NLTW+nVP/eFP1CvOYQQ==",
       "dev": true,
       "dependencies": {
-        "pako": "^1.0.5"
+        "pako": "^1.0.11"
       }
     },
     "node_modules/util-deprecate": {
@@ -3721,6 +3914,18 @@
         }
       }
     },
+    "node_modules/vscode-oniguruma": {
+      "version": "1.7.0",
+      "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz",
+      "integrity": "sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==",
+      "dev": true
+    },
+    "node_modules/vscode-textmate": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-8.0.0.tgz",
+      "integrity": "sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==",
+      "dev": true
+    },
     "node_modules/web-streams-polyfill": {
       "version": "3.2.1",
       "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz",
@@ -3764,6 +3969,12 @@
       "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
       "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
     },
+    "node_modules/whatwg-fetch": {
+      "version": "3.6.2",
+      "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz",
+      "integrity": "sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA==",
+      "dev": true
+    },
     "node_modules/whatwg-url": {
       "version": "5.0.0",
       "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
@@ -3855,15 +4066,6 @@
     }
   },
   "dependencies": {
-    "@babel/runtime": {
-      "version": "7.20.13",
-      "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.13.tgz",
-      "integrity": "sha512-gt3PKXs0DBoL9xCvOIIZ2NEqAGZqHjAnmVbfQtB620V0uReIQutpel14KcneZuer7UioY8ALKZ7iocavvzTNFA==",
-      "dev": true,
-      "requires": {
-        "regenerator-runtime": "^0.13.11"
-      }
-    },
     "@esbuild/android-arm": {
       "version": "0.16.17",
       "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.16.17.tgz",
@@ -4019,354 +4221,330 @@
       "optional": true
     },
     "@jimp/bmp": {
-      "version": "0.16.2",
-      "resolved": "https://registry.npmjs.org/@jimp/bmp/-/bmp-0.16.2.tgz",
-      "integrity": "sha512-4g9vW45QfMoGhLVvaFj26h4e7cC+McHUQwyFQmNTLW4FfC1OonN9oUr2m/FEDGkTYKR7aqdXR5XUqqIkHWLaFw==",
+      "version": "0.22.4",
+      "resolved": "https://registry.npmjs.org/@jimp/bmp/-/bmp-0.22.4.tgz",
+      "integrity": "sha512-ZDwQ/tLihpZuTCFGGa0zcyZIWHfhvHkrdbsoHUY0GG/JpH/y2xzlm2I48/TicCpoujN8oGKLHIJje0HmVX3xaA==",
       "dev": true,
       "requires": {
-        "@babel/runtime": "^7.7.2",
-        "@jimp/utils": "^0.16.2",
+        "@jimp/utils": "^0.22.4",
         "bmp-js": "^0.1.0"
       }
     },
     "@jimp/core": {
-      "version": "0.16.2",
-      "resolved": "https://registry.npmjs.org/@jimp/core/-/core-0.16.2.tgz",
-      "integrity": "sha512-dp7HcyUMzjXphXYodI6PaXue+I9PXAavbb+AN+1XqFbotN22Z12DosNPEyy+UhLY/hZiQQqUkEaJHkvV31rs+w==",
+      "version": "0.22.4",
+      "resolved": "https://registry.npmjs.org/@jimp/core/-/core-0.22.4.tgz",
+      "integrity": "sha512-K7guEYpXV44SCLR35QdPyKqF+mFZaEUAqiSL8qQ/F4N4Ws9JkPzFI/qYTjOkDoKxSWkXlKnlsk1sfMzy0yqA5g==",
       "dev": true,
       "requires": {
-        "@babel/runtime": "^7.7.2",
-        "@jimp/utils": "^0.16.2",
+        "@jimp/utils": "^0.22.4",
         "any-base": "^1.1.0",
         "buffer": "^5.2.0",
         "exif-parser": "^0.1.12",
-        "file-type": "^9.0.0",
-        "load-bmfont": "^1.3.1",
-        "mkdirp": "^0.5.1",
-        "phin": "^2.9.1",
+        "file-type": "^16.5.4",
+        "isomorphic-fetch": "^3.0.0",
+        "mkdirp": "^2.1.3",
         "pixelmatch": "^4.0.2",
-        "tinycolor2": "^1.4.1"
+        "tinycolor2": "^1.6.0"
+      },
+      "dependencies": {
+        "mkdirp": {
+          "version": "2.1.3",
+          "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-2.1.3.tgz",
+          "integrity": "sha512-sjAkg21peAG9HS+Dkx7hlG9Ztx7HLeKnvB3NQRcu/mltCVmvkF0pisbiTSfDVYTT86XEfZrTUosLdZLStquZUw==",
+          "dev": true
+        }
       }
     },
     "@jimp/custom": {
-      "version": "0.16.2",
-      "resolved": "https://registry.npmjs.org/@jimp/custom/-/custom-0.16.2.tgz",
-      "integrity": "sha512-GtNwOs4hcVS2GIbqRUf42rUuX07oLB92cj7cqxZb0ZGWwcwhnmSW0TFLAkNafXmqn9ug4VTpNvcJSUdiuECVKg==",
+      "version": "0.22.4",
+      "resolved": "https://registry.npmjs.org/@jimp/custom/-/custom-0.22.4.tgz",
+      "integrity": "sha512-k9m/RfxjPjklUsgZ2nszlyNkodUG/4xlxlif70UELhxW8bdqZqqlQGzwA9p+PUiSnlSJYZjL6q+P8cd7yj1ggA==",
       "dev": true,
       "requires": {
-        "@babel/runtime": "^7.7.2",
-        "@jimp/core": "^0.16.2"
+        "@jimp/core": "^0.22.4"
       }
     },
     "@jimp/gif": {
-      "version": "0.16.2",
-      "resolved": "https://registry.npmjs.org/@jimp/gif/-/gif-0.16.2.tgz",
-      "integrity": "sha512-TMdyT9Q0paIKNtT7c5KzQD29CNCsI/t8ka28jMrBjEK7j5RRTvBfuoOnHv7pDJRCjCIqeUoaUSJ7QcciKic6CA==",
+      "version": "0.22.4",
+      "resolved": "https://registry.npmjs.org/@jimp/gif/-/gif-0.22.4.tgz",
+      "integrity": "sha512-KmN7GoaQTzLAX4JXLBRkIiZAXthgQdKe+Y7BOw4n6CMe6LAS/XCQqrYCG3Av/GqIO7UAKems6D7kIGAUuhpNlQ==",
       "dev": true,
       "requires": {
-        "@babel/runtime": "^7.7.2",
-        "@jimp/utils": "^0.16.2",
+        "@jimp/utils": "^0.22.4",
         "gifwrap": "^0.9.2",
         "omggif": "^1.0.9"
       }
     },
     "@jimp/jpeg": {
-      "version": "0.16.2",
-      "resolved": "https://registry.npmjs.org/@jimp/jpeg/-/jpeg-0.16.2.tgz",
-      "integrity": "sha512-BW5gZydgq6wdIwHd+3iUNgrTklvoQc/FUKSj9meM6A0FU21lUaansRX5BDdJqHkyXJLnnlDGwDt27J+hQuBAVw==",
+      "version": "0.22.4",
+      "resolved": "https://registry.npmjs.org/@jimp/jpeg/-/jpeg-0.22.4.tgz",
+      "integrity": "sha512-mMJNhEtJpne65mxpIXEvT0VIzmsKiZWmaFT/c2eQ2tBLEtWAFpkvoP+F7jEaU+F3Ur4fXKFkJ/xOSXtRr/gGNw==",
       "dev": true,
       "requires": {
-        "@babel/runtime": "^7.7.2",
-        "@jimp/utils": "^0.16.2",
-        "jpeg-js": "^0.4.2"
+        "@jimp/utils": "^0.22.4",
+        "jpeg-js": "^0.4.4"
       }
     },
     "@jimp/plugin-blit": {
-      "version": "0.16.2",
-      "resolved": "https://registry.npmjs.org/@jimp/plugin-blit/-/plugin-blit-0.16.2.tgz",
-      "integrity": "sha512-Z31rRfV80gC/r+B/bOPSVVpJEWXUV248j7MdnMOFLu4vr8DMqXVo9jYqvwU/s4LSTMAMXqm4Jg6E/jQfadPKAg==",
+      "version": "0.22.4",
+      "resolved": "https://registry.npmjs.org/@jimp/plugin-blit/-/plugin-blit-0.22.4.tgz",
+      "integrity": "sha512-QQHe+rFarsxJQxWKlyHEMfLyXmUG9AiQky+8WfwjZVBYilIFyiBywOc3sThonOsru+7LOSUDmbN6mvbFk4R+gw==",
       "dev": true,
       "requires": {
-        "@babel/runtime": "^7.7.2",
-        "@jimp/utils": "^0.16.2"
+        "@jimp/utils": "^0.22.4"
       }
     },
     "@jimp/plugin-blur": {
-      "version": "0.16.2",
-      "resolved": "https://registry.npmjs.org/@jimp/plugin-blur/-/plugin-blur-0.16.2.tgz",
-      "integrity": "sha512-ShkJCAzRI+1fAKPuLLgEkixpSpVmKTYaKEFROUcgmrv9AansDXGNCupchqVMTdxf8zPyW8rR1ilvG3OJobufLQ==",
+      "version": "0.22.4",
+      "resolved": "https://registry.npmjs.org/@jimp/plugin-blur/-/plugin-blur-0.22.4.tgz",
+      "integrity": "sha512-p57Ac5LEQckIogiwf7qyOojGvLOD08eMaQd5ylOhet/fbdwAzD/8flWFhSIKsdAVzvnfGcszuLtrsV07jDutTw==",
       "dev": true,
       "requires": {
-        "@babel/runtime": "^7.7.2",
-        "@jimp/utils": "^0.16.2"
+        "@jimp/utils": "^0.22.4"
       }
     },
     "@jimp/plugin-circle": {
-      "version": "0.16.2",
-      "resolved": "https://registry.npmjs.org/@jimp/plugin-circle/-/plugin-circle-0.16.2.tgz",
-      "integrity": "sha512-6T4z/48F4Z5+YwAVCLOvXQcyGmo0E3WztxCz6XGQf66r4JJK78+zcCDYZFLMx0BGM0091FogNK4QniP8JaOkrA==",
+      "version": "0.22.4",
+      "resolved": "https://registry.npmjs.org/@jimp/plugin-circle/-/plugin-circle-0.22.4.tgz",
+      "integrity": "sha512-T+TpG+s+wM9kKHlpIEfCAfOM+QrYVqcMoWjkULddc0KtaDEhqgGYFhN+/SlzJfDbZKw0xUgIuAw89sXuzMIUjw==",
       "dev": true,
       "requires": {
-        "@babel/runtime": "^7.7.2",
-        "@jimp/utils": "^0.16.2"
+        "@jimp/utils": "^0.22.4"
       }
     },
     "@jimp/plugin-color": {
-      "version": "0.16.2",
-      "resolved": "https://registry.npmjs.org/@jimp/plugin-color/-/plugin-color-0.16.2.tgz",
-      "integrity": "sha512-6oBV0g0J17/7E+aTquvUsgSc85nUbUi+64tIK5eFIDzvjhlqhjGNJYlc46KJMCWIs61qRJayQoZdL/iT/iQuGQ==",
+      "version": "0.22.4",
+      "resolved": "https://registry.npmjs.org/@jimp/plugin-color/-/plugin-color-0.22.4.tgz",
+      "integrity": "sha512-TZqcqepoCcIlF7VodPPfS3WET+LL5Y/XnXOBk4tWnG5i+lhNrs7/U0HOJY6Iw9o4g267DddnlfKWmunvzBcvOQ==",
       "dev": true,
       "requires": {
-        "@babel/runtime": "^7.7.2",
-        "@jimp/utils": "^0.16.2",
-        "tinycolor2": "^1.4.1"
+        "@jimp/utils": "^0.22.4",
+        "tinycolor2": "^1.6.0"
       }
     },
     "@jimp/plugin-contain": {
-      "version": "0.16.2",
-      "resolved": "https://registry.npmjs.org/@jimp/plugin-contain/-/plugin-contain-0.16.2.tgz",
-      "integrity": "sha512-pLcxO3hVN3LCEhMNvpZ9B7xILHVlS433Vv16zFFJxLRqZdYvPLsc+ZzJhjAiHHuEjVblQrktHE3LGeQwGJPo0w==",
+      "version": "0.22.4",
+      "resolved": "https://registry.npmjs.org/@jimp/plugin-contain/-/plugin-contain-0.22.4.tgz",
+      "integrity": "sha512-Hl+TO4v+EpRfEl3R6k/bEgOGOpm6JqNfEIyCFWLi6yqJQjMGzBQ0vt+VHe2u3WIFaFrDWsGxeuFZBDzgtjTwxw==",
       "dev": true,
       "requires": {
-        "@babel/runtime": "^7.7.2",
-        "@jimp/utils": "^0.16.2"
+        "@jimp/utils": "^0.22.4"
       }
     },
     "@jimp/plugin-cover": {
-      "version": "0.16.2",
-      "resolved": "https://registry.npmjs.org/@jimp/plugin-cover/-/plugin-cover-0.16.2.tgz",
-      "integrity": "sha512-gzWM7VvYeI8msyiwbUZxH+sGQEgO6Vd6adGxZ0CeKX00uQOe5lDzxb1Wjx7sHcJGz8a/5fmAuwz7rdDtpDUbkw==",
+      "version": "0.22.4",
+      "resolved": "https://registry.npmjs.org/@jimp/plugin-cover/-/plugin-cover-0.22.4.tgz",
+      "integrity": "sha512-KMTQjN/B7r/RNzoLFwnhqhLrgT0kMqTkBMEZQSopj5vPLPNjIX0ElEYC8AIVFKeZAV+1mYkyss+IDdxq4fyRng==",
       "dev": true,
       "requires": {
-        "@babel/runtime": "^7.7.2",
-        "@jimp/utils": "^0.16.2"
+        "@jimp/utils": "^0.22.4"
       }
     },
     "@jimp/plugin-crop": {
-      "version": "0.16.2",
-      "resolved": "https://registry.npmjs.org/@jimp/plugin-crop/-/plugin-crop-0.16.2.tgz",
-      "integrity": "sha512-qCd3hfMEE+Z2EuuyXewgXRTtKJGIerWzc1zLEJztsUkPz5i73IGgkOL+mrNutZwGaXZbm+8SwUaGb46sxAO6Tw==",
+      "version": "0.22.4",
+      "resolved": "https://registry.npmjs.org/@jimp/plugin-crop/-/plugin-crop-0.22.4.tgz",
+      "integrity": "sha512-8krDt7xzBa1fbtlYCzEMZIgNjTkhgywho0FJpgIMkIUMjaZITS1Ea/Veb3UrWt8EsgQS6hxjGVE/Q1FvP5iPLA==",
       "dev": true,
       "requires": {
-        "@babel/runtime": "^7.7.2",
-        "@jimp/utils": "^0.16.2"
+        "@jimp/utils": "^0.22.4"
       }
     },
     "@jimp/plugin-displace": {
-      "version": "0.16.2",
-      "resolved": "https://registry.npmjs.org/@jimp/plugin-displace/-/plugin-displace-0.16.2.tgz",
-      "integrity": "sha512-6nXdvNNjCdD95v2o3/jPeur903dz08lG4Y8gmr5oL2yVv9LSSbMonoXYrR/ASesdyXqGdXJLU4NL+yZs4zUqbQ==",
+      "version": "0.22.4",
+      "resolved": "https://registry.npmjs.org/@jimp/plugin-displace/-/plugin-displace-0.22.4.tgz",
+      "integrity": "sha512-3gBfwYVFrOjp8SUpb7H0UMgqvsG/sxY1PVBIfRW9MUCosiH1eE/Mo5cbxhQ6/w5f3sh23lBmG8W0WuSrnXLorg==",
       "dev": true,
       "requires": {
-        "@babel/runtime": "^7.7.2",
-        "@jimp/utils": "^0.16.2"
+        "@jimp/utils": "^0.22.4"
       }
     },
     "@jimp/plugin-dither": {
-      "version": "0.16.2",
-      "resolved": "https://registry.npmjs.org/@jimp/plugin-dither/-/plugin-dither-0.16.2.tgz",
-      "integrity": "sha512-DERpIzy21ZanMkVsD0Tdy8HQLbD1E41OuvIzaMRoW4183PA6AgGNlrQoFTyXmzjy6FTy1SxaQgTEdouInAWZ9Q==",
+      "version": "0.22.4",
+      "resolved": "https://registry.npmjs.org/@jimp/plugin-dither/-/plugin-dither-0.22.4.tgz",
+      "integrity": "sha512-oOhdZBDJpSGIoTUhPOIvLIVUwILRWgrWdA4Vbzcyz2RHvaPHS8gdBH0EdIPbJ5agNyFnY8sJWFM7YKx/rLNKsw==",
       "dev": true,
       "requires": {
-        "@babel/runtime": "^7.7.2",
-        "@jimp/utils": "^0.16.2"
+        "@jimp/utils": "^0.22.4"
       }
     },
     "@jimp/plugin-fisheye": {
-      "version": "0.16.2",
-      "resolved": "https://registry.npmjs.org/@jimp/plugin-fisheye/-/plugin-fisheye-0.16.2.tgz",
-      "integrity": "sha512-Df7PsGIwiIpQu3EygYCnaJyTfOwvwtYV3cmYJS7yFLtdiFUuod+hlSo5GkwEPLAy+QBxhUbDuUqnsWo4NQtbiQ==",
+      "version": "0.22.4",
+      "resolved": "https://registry.npmjs.org/@jimp/plugin-fisheye/-/plugin-fisheye-0.22.4.tgz",
+      "integrity": "sha512-2myNZyDrwUOV8MKd4NeULnEOojYF7XRbnRHiUPsNptpmK6g/gI4xt+5k7BallAYZD8ZLfZVjstUogsObprHd/Q==",
       "dev": true,
       "requires": {
-        "@babel/runtime": "^7.7.2",
-        "@jimp/utils": "^0.16.2"
+        "@jimp/utils": "^0.22.4"
       }
     },
     "@jimp/plugin-flip": {
-      "version": "0.16.2",
-      "resolved": "https://registry.npmjs.org/@jimp/plugin-flip/-/plugin-flip-0.16.2.tgz",
-      "integrity": "sha512-+2uC8ioVQUr06mnjSWraskz2L33nJHze35LkQ8ZNsIpoZLkgvfiWatqAs5bj+1jGI/9kxoCFAaT1Is0f+a4/rw==",
+      "version": "0.22.4",
+      "resolved": "https://registry.npmjs.org/@jimp/plugin-flip/-/plugin-flip-0.22.4.tgz",
+      "integrity": "sha512-9FZ0k5N5leLDefeDjizXXTl17dzo23PYtCD/T4xeSVr96d1pQDwbeIk7pEhhHr1rl98tJe0U/OV2dFXFYauKPw==",
       "dev": true,
       "requires": {
-        "@babel/runtime": "^7.7.2",
-        "@jimp/utils": "^0.16.2"
+        "@jimp/utils": "^0.22.4"
       }
     },
     "@jimp/plugin-gaussian": {
-      "version": "0.16.2",
-      "resolved": "https://registry.npmjs.org/@jimp/plugin-gaussian/-/plugin-gaussian-0.16.2.tgz",
-      "integrity": "sha512-2mnuDSg4ZEH8zcJig7DZZf4st/cYmQ5UYJKP76iGhZ+6JDACk6uejwAgT5xHecNhkVAaXMdCybA2eknH/9OE1w==",
+      "version": "0.22.4",
+      "resolved": "https://registry.npmjs.org/@jimp/plugin-gaussian/-/plugin-gaussian-0.22.4.tgz",
+      "integrity": "sha512-irOSwLdZ9kTq5Wd5dpkMgIMJVwemYcqgnzd04+P6TJGYmem2HR6JUCDpjbET3Fpbo/snFLm4mZ+2A+SmeCGjKA==",
       "dev": true,
       "requires": {
-        "@babel/runtime": "^7.7.2",
-        "@jimp/utils": "^0.16.2"
+        "@jimp/utils": "^0.22.4"
       }
     },
     "@jimp/plugin-invert": {
-      "version": "0.16.2",
-      "resolved": "https://registry.npmjs.org/@jimp/plugin-invert/-/plugin-invert-0.16.2.tgz",
-      "integrity": "sha512-xFvHbVepTY/nus+6yXiYN1iq+UBRkT0MdnObbiQPstUrAsz0Imn6MWISsnAyMvcNxHGrxaxjuU777JT/esM0gg==",
+      "version": "0.22.4",
+      "resolved": "https://registry.npmjs.org/@jimp/plugin-invert/-/plugin-invert-0.22.4.tgz",
+      "integrity": "sha512-/WtZeLrF+H+mzbjqudeGvvSxudlHy1kyiP1gVWDxhYNQOuZJI57Vn20kSTYvHBNjvy31LV4/uestyX8j8tE2Qg==",
       "dev": true,
       "requires": {
-        "@babel/runtime": "^7.7.2",
-        "@jimp/utils": "^0.16.2"
+        "@jimp/utils": "^0.22.4"
       }
     },
     "@jimp/plugin-mask": {
-      "version": "0.16.2",
-      "resolved": "https://registry.npmjs.org/@jimp/plugin-mask/-/plugin-mask-0.16.2.tgz",
-      "integrity": "sha512-AbdO85xxhfgEDdxYKpUotEI9ixiCMaIpfYHD5a5O/VWeimz2kuwhcrzlHGiyq1kKAgRcl0WEneTCZAHVSyvPKA==",
+      "version": "0.22.4",
+      "resolved": "https://registry.npmjs.org/@jimp/plugin-mask/-/plugin-mask-0.22.4.tgz",
+      "integrity": "sha512-U0SrOwBNKkMYTNPTz5CXeJdZ4c5easFlq2B9Txy0kPsav2OraTv8cZjpMxrWUejo/AQGVUDbaGtXMm9pE13/6w==",
       "dev": true,
       "requires": {
-        "@babel/runtime": "^7.7.2",
-        "@jimp/utils": "^0.16.2"
+        "@jimp/utils": "^0.22.4"
       }
     },
     "@jimp/plugin-normalize": {
-      "version": "0.16.2",
-      "resolved": "https://registry.npmjs.org/@jimp/plugin-normalize/-/plugin-normalize-0.16.2.tgz",
-      "integrity": "sha512-+ItBWFwmB0Od7OfOtTYT1gm543PpHUgU8/DN55z83l1JqS0OomDJAe7BmCppo2405TN6YtVm/csXo7p4iWd/SQ==",
+      "version": "0.22.4",
+      "resolved": "https://registry.npmjs.org/@jimp/plugin-normalize/-/plugin-normalize-0.22.4.tgz",
+      "integrity": "sha512-XJiPBJGCHWmIzUdmL4mWP1Ev5LMp77oMmPXdgQGDty1cxfyo3CbkTjZSsnwF/XLlrQ1yfLW+8JB+ihGKcVEOxA==",
       "dev": true,
       "requires": {
-        "@babel/runtime": "^7.7.2",
-        "@jimp/utils": "^0.16.2"
+        "@jimp/utils": "^0.22.4"
       }
     },
     "@jimp/plugin-print": {
-      "version": "0.16.2",
-      "resolved": "https://registry.npmjs.org/@jimp/plugin-print/-/plugin-print-0.16.2.tgz",
-      "integrity": "sha512-ifTGEeJ5UZTCiqC70HMeU3iXk/vsOmhWiwVGOXSFXhFeE8ZpDWvlmBsrMYnRrJGuaaogHOIrrQPI+kCdDBSBIQ==",
+      "version": "0.22.4",
+      "resolved": "https://registry.npmjs.org/@jimp/plugin-print/-/plugin-print-0.22.4.tgz",
+      "integrity": "sha512-mayiPhg6c7KYjvq3fYOW9ohhXD1eWdEiseV9dAWqTOEbDbohT8S6eTGhVIiVa2sVySLcpNEKZSk07c5EhJAMcw==",
       "dev": true,
       "requires": {
-        "@babel/runtime": "^7.7.2",
-        "@jimp/utils": "^0.16.2",
-        "load-bmfont": "^1.4.0"
+        "@jimp/utils": "^0.22.4",
+        "load-bmfont": "^1.4.1"
       }
     },
     "@jimp/plugin-resize": {
-      "version": "0.16.2",
-      "resolved": "https://registry.npmjs.org/@jimp/plugin-resize/-/plugin-resize-0.16.2.tgz",
-      "integrity": "sha512-gE4N9l6xuwzacFZ2EPCGZCJ/xR+aX2V7GdMndIl/6kYIw5/eib1SFuF9AZLvIPSFuE1FnGo8+vT0pr++SSbhYg==",
+      "version": "0.22.4",
+      "resolved": "https://registry.npmjs.org/@jimp/plugin-resize/-/plugin-resize-0.22.4.tgz",
+      "integrity": "sha512-2wMdpPNGf6Zo2lfJg1QHHQ+ds5baQH75IcFpdjw717dcEISpn1jPG//iClXOGh16OJsRQlwHESaZTgEo/5Dw/g==",
       "dev": true,
       "requires": {
-        "@babel/runtime": "^7.7.2",
-        "@jimp/utils": "^0.16.2"
+        "@jimp/utils": "^0.22.4"
       }
     },
     "@jimp/plugin-rotate": {
-      "version": "0.16.2",
-      "resolved": "https://registry.npmjs.org/@jimp/plugin-rotate/-/plugin-rotate-0.16.2.tgz",
-      "integrity": "sha512-/CTEYkR1HrgmnE0VqPhhbBARbDAfFX590LWGIpxcYIYsUUGQCadl+8Qo4UX13FH0Nt8UHEtPA+O2x08uPYg9UA==",
+      "version": "0.22.4",
+      "resolved": "https://registry.npmjs.org/@jimp/plugin-rotate/-/plugin-rotate-0.22.4.tgz",
+      "integrity": "sha512-g08LBsPENbeA6NVoeq0iuDgAL89+N+aZrvYVKYkiJZIM7vUvueJyAIq4+bjDl4r54OR8XBFX0GsrKsqrULh1eA==",
       "dev": true,
       "requires": {
-        "@babel/runtime": "^7.7.2",
-        "@jimp/utils": "^0.16.2"
+        "@jimp/utils": "^0.22.4"
       }
     },
     "@jimp/plugin-scale": {
-      "version": "0.16.2",
-      "resolved": "https://registry.npmjs.org/@jimp/plugin-scale/-/plugin-scale-0.16.2.tgz",
-      "integrity": "sha512-3inuxfrlquyLaqFdiiiQNJUurR0WbvN5wAf1qcYX2LubG1AG8grayYD6H7XVoxfUGTZXh1kpmeirEYlqA2zxcw==",
+      "version": "0.22.4",
+      "resolved": "https://registry.npmjs.org/@jimp/plugin-scale/-/plugin-scale-0.22.4.tgz",
+      "integrity": "sha512-cJiLQtTcNk6/+j05R23TFGXy+smDV0BdlmzJVDb+7Ye9qcmWpkdjVSioQQqZr0QScIYKhhRCY/lFTepBx67yzw==",
       "dev": true,
       "requires": {
-        "@babel/runtime": "^7.7.2",
-        "@jimp/utils": "^0.16.2"
+        "@jimp/utils": "^0.22.4"
       }
     },
     "@jimp/plugin-shadow": {
-      "version": "0.16.2",
-      "resolved": "https://registry.npmjs.org/@jimp/plugin-shadow/-/plugin-shadow-0.16.2.tgz",
-      "integrity": "sha512-Q0aIs2/L6fWMcEh9Ms73u34bT1hyUMw/oxaVoIzOLo6/E8YzCs2Bi63H0/qaPS0MQpEppI++kvosPbblABY79w==",
+      "version": "0.22.4",
+      "resolved": "https://registry.npmjs.org/@jimp/plugin-shadow/-/plugin-shadow-0.22.4.tgz",
+      "integrity": "sha512-a5hdpzGBzBo91DNiKaGvs8iJbs2bYQmDRm/BrCh4NET+h5l5AwXNu/Ak0bWRhN16YQ55XYNGHer2jOwBPrf2WQ==",
       "dev": true,
       "requires": {
-        "@babel/runtime": "^7.7.2",
-        "@jimp/utils": "^0.16.2"
+        "@jimp/utils": "^0.22.4"
       }
     },
     "@jimp/plugin-threshold": {
-      "version": "0.16.2",
-      "resolved": "https://registry.npmjs.org/@jimp/plugin-threshold/-/plugin-threshold-0.16.2.tgz",
-      "integrity": "sha512-gyOwmBgjtMPvcuyOhkP6dOGWbQdaTfhcBRN22mYeI/k/Wh/Zh1OI21F6eKLApsVRmg15MoFnkrCz64RROC34sw==",
+      "version": "0.22.4",
+      "resolved": "https://registry.npmjs.org/@jimp/plugin-threshold/-/plugin-threshold-0.22.4.tgz",
+      "integrity": "sha512-jTT/+p2zb2NESzd7O0bVRowiQszoaHeBf2OgP7lFde10fHd+fn78m5brUmSmlGAdlMRwm8S8ZcxTj5ZjdQns5w==",
       "dev": true,
       "requires": {
-        "@babel/runtime": "^7.7.2",
-        "@jimp/utils": "^0.16.2"
+        "@jimp/utils": "^0.22.4"
       }
     },
     "@jimp/plugins": {
-      "version": "0.16.2",
-      "resolved": "https://registry.npmjs.org/@jimp/plugins/-/plugins-0.16.2.tgz",
-      "integrity": "sha512-zCvYtCgctmC0tkYEu+y+kSwSIZBsNznqJ3/3vkpzxdyjd6wCfNY5Qc/68MPrLc1lmdeGo4cOOTYHG7Vc6myzRw==",
-      "dev": true,
-      "requires": {
-        "@babel/runtime": "^7.7.2",
-        "@jimp/plugin-blit": "^0.16.2",
-        "@jimp/plugin-blur": "^0.16.2",
-        "@jimp/plugin-circle": "^0.16.2",
-        "@jimp/plugin-color": "^0.16.2",
-        "@jimp/plugin-contain": "^0.16.2",
-        "@jimp/plugin-cover": "^0.16.2",
-        "@jimp/plugin-crop": "^0.16.2",
-        "@jimp/plugin-displace": "^0.16.2",
-        "@jimp/plugin-dither": "^0.16.2",
-        "@jimp/plugin-fisheye": "^0.16.2",
-        "@jimp/plugin-flip": "^0.16.2",
-        "@jimp/plugin-gaussian": "^0.16.2",
-        "@jimp/plugin-invert": "^0.16.2",
-        "@jimp/plugin-mask": "^0.16.2",
-        "@jimp/plugin-normalize": "^0.16.2",
-        "@jimp/plugin-print": "^0.16.2",
-        "@jimp/plugin-resize": "^0.16.2",
-        "@jimp/plugin-rotate": "^0.16.2",
-        "@jimp/plugin-scale": "^0.16.2",
-        "@jimp/plugin-shadow": "^0.16.2",
-        "@jimp/plugin-threshold": "^0.16.2",
+      "version": "0.22.4",
+      "resolved": "https://registry.npmjs.org/@jimp/plugins/-/plugins-0.22.4.tgz",
+      "integrity": "sha512-yAxcA4UR3Bs7j73I7wt4ty52vm5MzPmr+8DYk8jrS/ng2Z2iuXzbcTe4mf9eEqXYVah3rTIggo4dPjW75DRZtA==",
+      "dev": true,
+      "requires": {
+        "@jimp/plugin-blit": "^0.22.4",
+        "@jimp/plugin-blur": "^0.22.4",
+        "@jimp/plugin-circle": "^0.22.4",
+        "@jimp/plugin-color": "^0.22.4",
+        "@jimp/plugin-contain": "^0.22.4",
+        "@jimp/plugin-cover": "^0.22.4",
+        "@jimp/plugin-crop": "^0.22.4",
+        "@jimp/plugin-displace": "^0.22.4",
+        "@jimp/plugin-dither": "^0.22.4",
+        "@jimp/plugin-fisheye": "^0.22.4",
+        "@jimp/plugin-flip": "^0.22.4",
+        "@jimp/plugin-gaussian": "^0.22.4",
+        "@jimp/plugin-invert": "^0.22.4",
+        "@jimp/plugin-mask": "^0.22.4",
+        "@jimp/plugin-normalize": "^0.22.4",
+        "@jimp/plugin-print": "^0.22.4",
+        "@jimp/plugin-resize": "^0.22.4",
+        "@jimp/plugin-rotate": "^0.22.4",
+        "@jimp/plugin-scale": "^0.22.4",
+        "@jimp/plugin-shadow": "^0.22.4",
+        "@jimp/plugin-threshold": "^0.22.4",
         "timm": "^1.6.1"
       }
     },
     "@jimp/png": {
-      "version": "0.16.2",
-      "resolved": "https://registry.npmjs.org/@jimp/png/-/png-0.16.2.tgz",
-      "integrity": "sha512-sFOtOSz/tzDwXEChFQ/Nxe+0+vG3Tj0eUxnZVDUG/StXE9dI8Bqmwj3MIa0EgK5s+QG3YlnDOmlPUa4JqmeYeQ==",
+      "version": "0.22.4",
+      "resolved": "https://registry.npmjs.org/@jimp/png/-/png-0.22.4.tgz",
+      "integrity": "sha512-kDovx9dTyV/TSR40HQHdRyVgNNb7Cny4/0PPEa+xeR7snuDC3dV5hu9s/QJwY0RMGiAkiuKDpiaBuSZuz8dwRQ==",
       "dev": true,
       "requires": {
-        "@babel/runtime": "^7.7.2",
-        "@jimp/utils": "^0.16.2",
-        "pngjs": "^3.3.3"
+        "@jimp/utils": "^0.22.4",
+        "pngjs": "^6.0.0"
       }
     },
     "@jimp/tiff": {
-      "version": "0.16.2",
-      "resolved": "https://registry.npmjs.org/@jimp/tiff/-/tiff-0.16.2.tgz",
-      "integrity": "sha512-ADcdqmtZF+U2YoaaHTzFX8D6NFpmN4WZUT0BPMerEuY7Cq8QoLYU22z2h034FrVW+Rbi1b3y04sB9iDiQAlf2w==",
+      "version": "0.22.4",
+      "resolved": "https://registry.npmjs.org/@jimp/tiff/-/tiff-0.22.4.tgz",
+      "integrity": "sha512-RStUATRnb+unYzzuGxU+SPZALqh5NxYdcS6UGTBvhCMlijopGiY/iL01wstIOst0ypKIjwbcSVj7mAHn6B7Qbw==",
       "dev": true,
       "requires": {
-        "@babel/runtime": "^7.7.2",
-        "utif": "^2.0.1"
+        "utif2": "^4.0.1"
       }
     },
     "@jimp/types": {
-      "version": "0.16.2",
-      "resolved": "https://registry.npmjs.org/@jimp/types/-/types-0.16.2.tgz",
-      "integrity": "sha512-0Ue5Sq0XnDF6TirisWv5E+8uOnRcd8vRLuwocJOhF76NIlcQrz+5r2k2XWKcr3d+11n28dHLXW5TKSqrUopxhA==",
+      "version": "0.22.4",
+      "resolved": "https://registry.npmjs.org/@jimp/types/-/types-0.22.4.tgz",
+      "integrity": "sha512-v3hm8LGc3we6P6ML0ticiLX7wtdvywrKthYxqVrJVIu3vRL0Z4q3ngFjwzqDmaIF8wC0neq98s/t7ODWfeIiRQ==",
       "dev": true,
       "requires": {
-        "@babel/runtime": "^7.7.2",
-        "@jimp/bmp": "^0.16.2",
-        "@jimp/gif": "^0.16.2",
-        "@jimp/jpeg": "^0.16.2",
-        "@jimp/png": "^0.16.2",
-        "@jimp/tiff": "^0.16.2",
+        "@jimp/bmp": "^0.22.4",
+        "@jimp/gif": "^0.22.4",
+        "@jimp/jpeg": "^0.22.4",
+        "@jimp/png": "^0.22.4",
+        "@jimp/tiff": "^0.22.4",
         "timm": "^1.6.1"
       }
     },
     "@jimp/utils": {
-      "version": "0.16.2",
-      "resolved": "https://registry.npmjs.org/@jimp/utils/-/utils-0.16.2.tgz",
-      "integrity": "sha512-XENrPvmigiXZQ8E2nxJqO6UVvWBLzbNwyYi3Y8Q1IECoYhYI3kgOQ0fmy4G269Vz1V0omh1bNmC42r4OfXg1Jg==",
+      "version": "0.22.4",
+      "resolved": "https://registry.npmjs.org/@jimp/utils/-/utils-0.22.4.tgz",
+      "integrity": "sha512-EPaBMNg4NvVXnMpSFJEsdCQqdSVU2ACreAL+Ipkq19C/FkDEj9Q10t6Mjx8zOe/AAjBQj1vTALS/DykcHOn4bQ==",
       "dev": true,
       "requires": {
-        "@babel/runtime": "^7.7.2",
         "regenerator-runtime": "^0.13.3"
       }
     },
@@ -4599,9 +4777,9 @@
       }
     },
     "@supabase/supabase-js": {
-      "version": "2.7.0",
-      "resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.7.0.tgz",
-      "integrity": "sha512-0Ry6rcxeya0VRbPh6fHfgPcmH7X9gMILon7/PWoVJSjYsQntv7EGUNNeHtLutBlm8St74s5Q4sjsqJOPslDG4Q==",
+      "version": "2.7.1",
+      "resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.7.1.tgz",
+      "integrity": "sha512-Q/e+JAluZEvy7D4ul3aAs3aOiKkGvHlZULy6wjchWQyU9YlJKZLr6VPYcwUeitcnRKZi4al5iTS55LgdJFfqIA==",
       "requires": {
         "@supabase/functions-js": "^2.0.0",
         "@supabase/gotrue-js": "^2.10.2",
@@ -4612,18 +4790,18 @@
       }
     },
     "@sveltejs/adapter-auto": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/@sveltejs/adapter-auto/-/adapter-auto-1.0.2.tgz",
-      "integrity": "sha512-UXpEO/gutERZnD+Z5Vi4J/ifD3WSRuCI7xwtLJTcKNQvJ6t5Xsj1X3Mw2F8Vv/XTUuxf7xPLYUgThU331r0Y9w==",
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/@sveltejs/adapter-auto/-/adapter-auto-2.0.0.tgz",
+      "integrity": "sha512-b+gkHFZgD771kgV3aO4avHFd7y1zhmMYy9i6xOK7m/rwmwaRO8gnF5zBc0Rgca80B2PMU1bKNxyBTHA14OzUAQ==",
       "dev": true,
       "requires": {
         "import-meta-resolve": "^2.2.0"
       }
     },
     "@sveltejs/kit": {
-      "version": "1.3.10",
-      "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-1.3.10.tgz",
-      "integrity": "sha512-I3DgWCwTYbTz4ZPCJIRkSDrKkMu0bsdk6ghqsOBVNqesf1wBdTdfkXhag3ESWgIEjUV3VUIWPQF7fnt7328mhQ==",
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-1.5.0.tgz",
+      "integrity": "sha512-AkWgCO9i2djZjTqCgIQJ5XfnSzRINowh2w2Gk9wDRuTwxKizSuYe3jNvds/HCDDGHo8XE5E0yWNC9j2XxbrX+g==",
       "dev": true,
       "requires": {
         "@sveltejs/vite-plugin-svelte": "^2.0.0",
@@ -4638,7 +4816,7 @@
         "set-cookie-parser": "^2.5.1",
         "sirv": "^2.0.2",
         "tiny-glob": "^0.2.9",
-        "undici": "5.16.0"
+        "undici": "5.18.0"
       }
     },
     "@sveltejs/repl": {
@@ -4666,9 +4844,9 @@
       }
     },
     "@sveltejs/site-kit": {
-      "version": "3.2.1",
-      "resolved": "https://registry.npmjs.org/@sveltejs/site-kit/-/site-kit-3.2.1.tgz",
-      "integrity": "sha512-TrWTMI/7JVHHKWR1Eb2H77FfyHSw9HjlYjSyK1Hk8ZTJe4aQsO0fr/J+bfGB+Iz33RU0PyerA60KHv47KdISUw==",
+      "version": "3.2.2",
+      "resolved": "https://registry.npmjs.org/@sveltejs/site-kit/-/site-kit-3.2.2.tgz",
+      "integrity": "sha512-HBLtfNdLr5Ykl8i8CvJcYjib7zMIJupg4T/omplp3ccpgpiUh26tk71vRG1+a6yMkfbfy0ShoPb9uwNril5cnw==",
       "dev": true
     },
     "@sveltejs/vite-plugin-svelte": {
@@ -4685,6 +4863,12 @@
         "vitefu": "^0.2.3"
       }
     },
+    "@tokenizer/token": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz",
+      "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==",
+      "dev": true
+    },
     "@types/cookie": {
       "version": "0.5.1",
       "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.5.1.tgz",
@@ -4696,6 +4880,12 @@
       "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.0.tgz",
       "integrity": "sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ=="
     },
+    "@types/marked": {
+      "version": "4.0.8",
+      "resolved": "https://registry.npmjs.org/@types/marked/-/marked-4.0.8.tgz",
+      "integrity": "sha512-HVNzMT5QlWCOdeuBsgXP8EZzKUf0+AXzN+sLmjvaB3ZlLqO+e4u0uXrdw9ub69wBKFs+c6/pA4r9sy6cCDvImw==",
+      "dev": true
+    },
     "@types/node": {
       "version": "16.9.1",
       "resolved": "https://registry.npmjs.org/@types/node/-/node-16.9.1.tgz",
@@ -4707,6 +4897,12 @@
       "resolved": "https://registry.npmjs.org/@types/phoenix/-/phoenix-1.5.4.tgz",
       "integrity": "sha512-L5eZmzw89eXBKkiqVBcJfU1QGx9y+wurRIEgt0cuLH0hwNtVUxtx+6cu0R2STwWj468sjXyBYPYDtGclUd1kjQ=="
     },
+    "@types/prismjs": {
+      "version": "1.26.0",
+      "resolved": "https://registry.npmjs.org/@types/prismjs/-/prismjs-1.26.0.tgz",
+      "integrity": "sha512-ZTaqn/qSqUuAq1YwvOFQfVW1AR/oQJlLSZVustdjwI+GZ8kr0MSHBj0tsXPW1EqHubx50gtBEjbPGsdZwQwCjQ==",
+      "dev": true
+    },
     "@types/pug": {
       "version": "2.0.6",
       "resolved": "https://registry.npmjs.org/@types/pug/-/pug-2.0.6.tgz",
@@ -4722,11 +4918,48 @@
         "@types/node": "*"
       }
     },
+    "@typescript/twoslash": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/@typescript/twoslash/-/twoslash-3.1.0.tgz",
+      "integrity": "sha512-kTwMUQ8xtAZaC4wb2XuLkPqFVBj2dNBueMQ89NWEuw87k2nLBbuafeG5cob/QEr6YduxIdTVUjix0MtC7mPlmg==",
+      "dev": true,
+      "requires": {
+        "@typescript/vfs": "1.3.5",
+        "debug": "^4.1.1",
+        "lz-string": "^1.4.4"
+      },
+      "dependencies": {
+        "@typescript/vfs": {
+          "version": "1.3.5",
+          "resolved": "https://registry.npmjs.org/@typescript/vfs/-/vfs-1.3.5.tgz",
+          "integrity": "sha512-pI8Saqjupf9MfLw7w2+og+fmb0fZS0J6vsKXXrp4/PDXEFvntgzXmChCXC/KefZZS0YGS6AT8e0hGAJcTsdJlg==",
+          "dev": true,
+          "requires": {
+            "debug": "^4.1.1"
+          }
+        }
+      }
+    },
+    "@typescript/vfs": {
+      "version": "1.3.4",
+      "resolved": "https://registry.npmjs.org/@typescript/vfs/-/vfs-1.3.4.tgz",
+      "integrity": "sha512-RbyJiaAGQPIcAGWFa3jAXSuAexU4BFiDRF1g3hy7LmRqfNpYlTQWGXjcrOaVZjJ8YkkpuwG0FcsYvtWQpd9igQ==",
+      "dev": true,
+      "requires": {
+        "debug": "^4.1.1"
+      }
+    },
     "acorn": {
       "version": "8.8.2",
       "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz",
       "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw=="
     },
+    "ansi-sequence-parser": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/ansi-sequence-parser/-/ansi-sequence-parser-1.1.0.tgz",
+      "integrity": "sha512-lEm8mt52to2fT8GhciPCGeCXACSz2UwIN4X2e2LJSnZ5uAbn2/dsYdOmUXq0AtWS5cpAupysIneExOgH0Vd2TQ==",
+      "dev": true
+    },
     "any-base": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/any-base/-/any-base-1.1.0.tgz",
@@ -5213,10 +5446,15 @@
       "dev": true
     },
     "file-type": {
-      "version": "9.0.0",
-      "resolved": "https://registry.npmjs.org/file-type/-/file-type-9.0.0.tgz",
-      "integrity": "sha512-Qe/5NJrgIOlwijpq3B7BEpzPFcgzggOTagZmkXQY4LA6bsXKTUstK7Wp12lEJ/mLKTpvIZxmIuRcLYWT6ov9lw==",
-      "dev": true
+      "version": "16.5.4",
+      "resolved": "https://registry.npmjs.org/file-type/-/file-type-16.5.4.tgz",
+      "integrity": "sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw==",
+      "dev": true,
+      "requires": {
+        "readable-web-to-node-stream": "^3.0.0",
+        "strtok3": "^6.2.4",
+        "token-types": "^4.1.1"
+      }
     },
     "fill-range": {
       "version": "7.0.1",
@@ -5470,16 +5708,36 @@
       "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
       "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA=="
     },
+    "isomorphic-fetch": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-3.0.0.tgz",
+      "integrity": "sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA==",
+      "dev": true,
+      "requires": {
+        "node-fetch": "^2.6.1",
+        "whatwg-fetch": "^3.4.1"
+      },
+      "dependencies": {
+        "node-fetch": {
+          "version": "2.6.9",
+          "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.9.tgz",
+          "integrity": "sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg==",
+          "dev": true,
+          "requires": {
+            "whatwg-url": "^5.0.0"
+          }
+        }
+      }
+    },
     "jimp": {
-      "version": "0.16.2",
-      "resolved": "https://registry.npmjs.org/jimp/-/jimp-0.16.2.tgz",
-      "integrity": "sha512-UpItBk81a92f8oEyoGYbO3YK4QcM0hoIyuGHmShoF9Ov63P5Qo7Q/X2xsAgnODmSuDJFOtrPtJd5GSWW4LKdOQ==",
+      "version": "0.22.4",
+      "resolved": "https://registry.npmjs.org/jimp/-/jimp-0.22.4.tgz",
+      "integrity": "sha512-reGESbcYp38VlGtdAe8qrmbjLLEYXMrQWc2XXb7+czulKfCCidUHEpNfrS3hx5XXMWrAmoYKkxPTqCvll6Q6ug==",
       "dev": true,
       "requires": {
-        "@babel/runtime": "^7.7.2",
-        "@jimp/custom": "^0.16.2",
-        "@jimp/plugins": "^0.16.2",
-        "@jimp/types": "^0.16.2",
+        "@jimp/custom": "^0.22.4",
+        "@jimp/plugins": "^0.22.4",
+        "@jimp/types": "^0.22.4",
         "regenerator-runtime": "^0.13.3"
       }
     },
@@ -5489,6 +5747,12 @@
       "integrity": "sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg==",
       "dev": true
     },
+    "jsonc-parser": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz",
+      "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==",
+      "dev": true
+    },
     "kleur": {
       "version": "4.1.5",
       "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz",
@@ -5528,6 +5792,12 @@
         "yallist": "^4.0.0"
       }
     },
+    "lz-string": {
+      "version": "1.4.4",
+      "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.4.4.tgz",
+      "integrity": "sha512-0ckx7ZHRPqb0oUm8zNr+90mtf9DQB60H1wMCjBtfi62Kl3a7JbHob6gA2bC+xRvZoOL+1hzUK8jeuEIQE8svEQ==",
+      "dev": true
+    },
     "magic-string": {
       "version": "0.27.0",
       "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.27.0.tgz",
@@ -5764,6 +6034,12 @@
       "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
       "dev": true
     },
+    "peek-readable": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-4.1.0.tgz",
+      "integrity": "sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg==",
+      "dev": true
+    },
     "phin": {
       "version": "2.9.3",
       "resolved": "https://registry.npmjs.org/phin/-/phin-2.9.3.tgz",
@@ -5789,12 +6065,20 @@
       "dev": true,
       "requires": {
         "pngjs": "^3.0.0"
+      },
+      "dependencies": {
+        "pngjs": {
+          "version": "3.4.0",
+          "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.4.0.tgz",
+          "integrity": "sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==",
+          "dev": true
+        }
       }
     },
     "pngjs": {
-      "version": "3.4.0",
-      "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.4.0.tgz",
-      "integrity": "sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==",
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-6.0.0.tgz",
+      "integrity": "sha512-TRzzuFRRmEoSW/p1KVAmiOgPco2Irlah+bGFCeNfJXxxYGwSw7YwAOAcd7X28K/m5bjBWKsC29KyoMfHbypayg==",
       "dev": true
     },
     "postcss": {
@@ -5904,6 +6188,15 @@
         "util-deprecate": "^1.0.1"
       }
     },
+    "readable-web-to-node-stream": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz",
+      "integrity": "sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==",
+      "dev": true,
+      "requires": {
+        "readable-stream": "^3.6.0"
+      }
+    },
     "readdirp": {
       "version": "3.6.0",
       "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
@@ -6005,9 +6298,9 @@
       }
     },
     "satori": {
-      "version": "0.1.2",
-      "resolved": "https://registry.npmjs.org/satori/-/satori-0.1.2.tgz",
-      "integrity": "sha512-1P08+PyNUzJ2P0bdI9reSJw+B/Whv9nThvoX4HAiAp9lNNTHtwtN9525TuEtELml7AEEvECpeOvBqMkAxcuQeQ==",
+      "version": "0.2.3",
+      "resolved": "https://registry.npmjs.org/satori/-/satori-0.2.3.tgz",
+      "integrity": "sha512-jl/+sFkz3pBfGip7034Uvn/Hp1FZq4prKXIZO28rV90DS3jTU6uXBV0FRX4ooYoO27f3rGNXKhSKyDST5FdmQA==",
       "dev": true,
       "requires": {
         "@shuding/opentype.js": "1.4.0-beta.0",
@@ -6076,6 +6369,49 @@
         "rechoir": "^0.6.2"
       }
     },
+    "shiki": {
+      "version": "0.14.0",
+      "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.14.0.tgz",
+      "integrity": "sha512-fb9Fg1Yx/ElVJcTqPQIEOSfn7mSZlrT1W3CkymY08lL2Jsi+t7jPcZzKO1lCsQwlSDuyNhHvolnyA2OI4EgJNg==",
+      "dev": true,
+      "requires": {
+        "ansi-sequence-parser": "^1.1.0",
+        "jsonc-parser": "^3.2.0",
+        "vscode-oniguruma": "^1.7.0",
+        "vscode-textmate": "^8.0.0"
+      }
+    },
+    "shiki-twoslash": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/shiki-twoslash/-/shiki-twoslash-3.1.0.tgz",
+      "integrity": "sha512-uDqrTutOIZzyHbo103GsK7Vvc10saK1XCCivnOQ1NHJzgp3FBilEpftGeVzVSMOJs+JyhI7whkvhXV7kXQ5zCg==",
+      "dev": true,
+      "requires": {
+        "@typescript/twoslash": "3.1.0",
+        "@typescript/vfs": "1.3.4",
+        "shiki": "0.10.1",
+        "typescript": ">3"
+      },
+      "dependencies": {
+        "shiki": {
+          "version": "0.10.1",
+          "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.10.1.tgz",
+          "integrity": "sha512-VsY7QJVzU51j5o1+DguUd+6vmCmZ5v/6gYu4vyYAhzjuNQU6P/vmSy4uQaOhvje031qQMiW0d2BwgMH52vqMng==",
+          "dev": true,
+          "requires": {
+            "jsonc-parser": "^3.0.0",
+            "vscode-oniguruma": "^1.6.1",
+            "vscode-textmate": "5.2.0"
+          }
+        },
+        "vscode-textmate": {
+          "version": "5.2.0",
+          "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-5.2.0.tgz",
+          "integrity": "sha512-Uw5ooOQxRASHgu6C7GVvUxisKXfSgW4oFlO+aa+PAkgmH89O3CXxEEzNRNtHSqtXFTl0nAC1uYj0GMSH27uwtQ==",
+          "dev": true
+        }
+      }
+    },
     "simple-concat": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz",
@@ -6172,6 +6508,16 @@
       "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==",
       "dev": true
     },
+    "strtok3": {
+      "version": "6.3.0",
+      "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-6.3.0.tgz",
+      "integrity": "sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw==",
+      "dev": true,
+      "requires": {
+        "@tokenizer/token": "^0.3.0",
+        "peek-readable": "^4.1.0"
+      }
+    },
     "supports-preserve-symlinks-flag": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
@@ -6268,9 +6614,9 @@
       }
     },
     "tinycolor2": {
-      "version": "1.5.2",
-      "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.5.2.tgz",
-      "integrity": "sha512-h80m9GPFGbcLzZByXlNSEhp1gf8Dy+VX/2JCGUZsWLo7lV1mnE/XlxGYgRBoMLJh1lIDXP0EMC4RPTjlRaV+Bg==",
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.6.0.tgz",
+      "integrity": "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==",
       "dev": true
     },
     "to-regex-range": {
@@ -6282,6 +6628,16 @@
         "is-number": "^7.0.0"
       }
     },
+    "token-types": {
+      "version": "4.2.1",
+      "resolved": "https://registry.npmjs.org/token-types/-/token-types-4.2.1.tgz",
+      "integrity": "sha512-6udB24Q737UD/SDsKAHI9FCRP7Bqc9D/MQUV02ORQg5iskjtLJlZJNdN4kKtcdtwCeWIwIHDGaUsTsCCAa8sFQ==",
+      "dev": true,
+      "requires": {
+        "@tokenizer/token": "^0.3.0",
+        "ieee754": "^1.2.1"
+      }
+    },
     "totalist": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.0.tgz",
@@ -6328,9 +6684,9 @@
       "dev": true
     },
     "undici": {
-      "version": "5.16.0",
-      "resolved": "https://registry.npmjs.org/undici/-/undici-5.16.0.tgz",
-      "integrity": "sha512-KWBOXNv6VX+oJQhchXieUznEmnJMqgXMbs0xxH2t8q/FUAWSJvOSr/rMaZKnX5RIVq7JDn0JbP4BOnKG2SGXLQ==",
+      "version": "5.18.0",
+      "resolved": "https://registry.npmjs.org/undici/-/undici-5.18.0.tgz",
+      "integrity": "sha512-1iVwbhonhFytNdg0P4PqyIAXbdlVZVebtPDvuM36m66mRw4OGrCm2MYynJv/UENFLdP13J1nPVQzVE2zTs1OeA==",
       "dev": true,
       "requires": {
         "busboy": "^1.6.0"
@@ -6344,13 +6700,13 @@
         "node-gyp-build": "^4.3.0"
       }
     },
-    "utif": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/utif/-/utif-2.0.1.tgz",
-      "integrity": "sha512-Z/S1fNKCicQTf375lIP9G8Sa1H/phcysstNrrSdZKj1f9g58J4NMgb5IgiEZN9/nLMPDwF0W7hdOe9Qq2IYoLg==",
+    "utif2": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/utif2/-/utif2-4.0.1.tgz",
+      "integrity": "sha512-KMaD76dbzK1VjbwsckHJiqDjhP3pbpwyV+FdqkY6XFQenc2o/HS6pjPSYdu4+NQMHf2NLTW+nVP/eFP1CvOYQQ==",
       "dev": true,
       "requires": {
-        "pako": "^1.0.5"
+        "pako": "^1.0.11"
       }
     },
     "util-deprecate": {
@@ -6400,6 +6756,18 @@
       "dev": true,
       "requires": {}
     },
+    "vscode-oniguruma": {
+      "version": "1.7.0",
+      "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz",
+      "integrity": "sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==",
+      "dev": true
+    },
+    "vscode-textmate": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-8.0.0.tgz",
+      "integrity": "sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==",
+      "dev": true
+    },
     "web-streams-polyfill": {
       "version": "3.2.1",
       "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz",
@@ -6439,6 +6807,12 @@
         }
       }
     },
+    "whatwg-fetch": {
+      "version": "3.6.2",
+      "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz",
+      "integrity": "sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA==",
+      "dev": true
+    },
     "whatwg-url": {
       "version": "5.0.0",
       "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
diff --git a/sites/svelte.dev/package.json b/sites/svelte.dev/package.json
index 9eb9420162..3ae104d38c 100644
--- a/sites/svelte.dev/package.json
+++ b/sites/svelte.dev/package.json
@@ -16,7 +16,7 @@
     "test": "uvu -r ts-node/register src/lib/server/markdown"
   },
   "dependencies": {
-    "@supabase/supabase-js": "^2.7.0",
+    "@supabase/supabase-js": "^2.7.1",
     "@sveltejs/repl": "0.0.3",
     "cookie": "^0.5.0",
     "devalue": "^4.2.3",
@@ -25,27 +25,31 @@
     "sourcemap-codec": "^1.4.8"
   },
   "devDependencies": {
-    "@resvg/resvg-js": "^2.2.0",
-    "@sveltejs/adapter-auto": "^1.0.2",
-    "@sveltejs/kit": "^1.3.10",
-    "@sveltejs/site-kit": "^3.2.1",
+    "@resvg/resvg-js": "^2.3.1",
+    "@sveltejs/adapter-auto": "^2.0.0",
+    "@sveltejs/kit": "^1.5.0",
+    "@sveltejs/site-kit": "^3.2.2",
     "@sveltejs/vite-plugin-svelte": "^2.0.2",
+    "@types/marked": "^4.0.8",
+    "@types/prismjs": "^1.26.0",
     "degit": "^2.8.4",
     "dotenv": "^16.0.3",
-    "jimp": "^0.16.2",
+    "jimp": "^0.22.4",
     "marked": "^4.2.12",
     "node-fetch": "^3.3.0",
     "prettier": "^2.8.3",
     "prettier-plugin-svelte": "^2.9.0",
     "prism-svelte": "^0.5.0",
     "prismjs": "^1.29.0",
-    "satori": "^0.1.2",
+    "satori": "^0.2.3",
     "satori-html": "^0.3.2",
     "shelljs": "^0.8.5",
+    "shiki": "^0.14.0",
+    "shiki-twoslash": "^3.1.0",
     "svelte": "^3.55.1",
     "svelte-check": "^3.0.3",
     "typescript": "^4.9.5",
-    "vite": "^4.0.4",
+    "vite": "^4.1.1",
     "vite-imagetools": "^4.0.18"
   }
 }
diff --git a/sites/svelte.dev/src/lib/server/blog/index.js b/sites/svelte.dev/src/lib/server/blog/index.js
new file mode 100644
index 0000000000..6e39380b6f
--- /dev/null
+++ b/sites/svelte.dev/src/lib/server/blog/index.js
@@ -0,0 +1,73 @@
+import fs from 'fs';
+import { extract_frontmatter } from '../markdown';
+import { transform } from './marked';
+
+/**
+ * @returns {import('./types').BlogPostSummary[]}
+ */
+export function get_index() {
+	return fs
+		.readdirSync('content/blog')
+		.reverse()
+		.map((file) => {
+			if (!file.endsWith('.md')) return;
+
+			const { date, slug } = get_date_and_slug(file);
+
+			const content = fs.readFileSync(`content/blog/${file}`, 'utf-8');
+			const { metadata } = extract_frontmatter(content);
+
+			return {
+				slug,
+				date,
+				title: metadata.title,
+				description: metadata.description,
+				draft: !!metadata.draft,
+			};
+		});
+}
+
+/**
+ * @param {string} slug
+ * @returns {import('./types').BlogPost}
+ */
+export function get_post(slug) {
+	for (const file of fs.readdirSync('content/blog')) {
+		if (!file.endsWith('.md')) continue;
+		if (file.slice(11, -3) !== slug) continue;
+
+		const { date, date_formatted } = get_date_and_slug(file);
+
+		const content = fs.readFileSync(`content/blog/${file}`, 'utf-8');
+		const { metadata, body } = extract_frontmatter(content);
+
+		return {
+			date,
+			date_formatted,
+			title: metadata.title,
+			description: metadata.description,
+			author: {
+				name: metadata.author,
+				url: metadata.authorURL,
+			},
+			draft: !!metadata.draft,
+			content: transform(body),
+		};
+	}
+}
+
+/** @param {string} filename */
+function get_date_and_slug(filename) {
+	const match = /^(\d{4}-\d{2}-\d{2})-(.+)\.md$/.exec(filename);
+	if (!match) throw new Error(`Invalid filename for blog: '${filename}'`);
+
+	const [, date, slug] = match;
+	const [y, m, d] = date.split('-');
+	const date_formatted = `${months[+m - 1]} ${+d} ${y}`;
+
+	return { date, date_formatted, slug };
+}
+
+const months = 'Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec'.split(' ');
+
+function format_date(date) {}
diff --git a/sites/svelte.dev/src/lib/server/markdown/marked.js b/sites/svelte.dev/src/lib/server/blog/marked.js
similarity index 100%
rename from sites/svelte.dev/src/lib/server/markdown/marked.js
rename to sites/svelte.dev/src/lib/server/blog/marked.js
diff --git a/sites/svelte.dev/src/lib/server/markdown/types.d.ts b/sites/svelte.dev/src/lib/server/blog/types.d.ts
similarity index 100%
rename from sites/svelte.dev/src/lib/server/markdown/types.d.ts
rename to sites/svelte.dev/src/lib/server/blog/types.d.ts
diff --git a/sites/svelte.dev/src/lib/server/docs/index.js b/sites/svelte.dev/src/lib/server/docs/index.js
new file mode 100644
index 0000000000..e408d9d6c8
--- /dev/null
+++ b/sites/svelte.dev/src/lib/server/docs/index.js
@@ -0,0 +1,570 @@
+import * as fs from 'fs';
+import * as path from 'path';
+import { renderCodeToHTML, runTwoSlash, createShikiHighlighter } from 'shiki-twoslash';
+import PrismJS from 'prismjs';
+import 'prismjs/components/prism-bash.js';
+import 'prismjs/components/prism-diff.js';
+import 'prismjs/components/prism-typescript.js';
+import 'prism-svelte';
+import { escape, extract_frontmatter, transform } from '../markdown';
+// import { render, replace_placeholders } from './render.js';
+// import { parse_route_id } from '../../../../../../packages/kit/src/utils/routing.js';
+import ts, { ScriptTarget } from 'typescript';
+import MagicString from 'magic-string';
+import { fileURLToPath } from 'url';
+import { createHash } from 'crypto';
+
+const base = '../../site/content/docs/';
+
+const languages = {
+	bash: 'bash',
+	env: 'bash',
+	html: 'html',
+	svelte: 'svelte',
+	sv: 'svelte',
+	js: 'javascript',
+	css: 'css',
+	diff: 'diff',
+	ts: 'typescript',
+	'': '',
+};
+
+/**
+ * @param {string} file
+ */
+export async function read_file(file) {
+	const match = /\d{2}-(.+)\.md/.exec(path.basename(file));
+	if (!match) return null;
+
+	// const markdown = replace_placeholders(fs.readFileSync(`${base}/${file}`, 'utf-8'));
+	const markdown = fs.readFileSync(`${base}/${file}`, 'utf-8');
+
+	const highlighter = await createShikiHighlighter({ theme: 'css-variables' });
+
+	const { metadata, body } = extract_frontmatter(markdown);
+
+	const { content, sections } = parse({
+		file,
+		body: generate_ts_from_js(body),
+		code: (source, language, current) => {
+			const hash = createHash('sha256');
+			hash.update(source + language + current);
+			const digest = hash.digest().toString('base64').replace(/\//g, '-');
+
+			// TODO: cache
+			// if (fs.existsSync(`${snippet_cache}/${digest}.html`)) {
+			// 	return fs.readFileSync(`${snippet_cache}/${digest}.html`, 'utf-8');
+			// }
+
+			/** @type {Record<string, string>} */
+			const options = {};
+
+			let html = '';
+
+			source = source
+				.replace(/^\/\/\/ (.+?): (.+)\n/gm, (_, key, value) => {
+					options[key] = value;
+					return '';
+				})
+				.replace(/^([\-\+])?((?:    )+)/gm, (match, prefix = '', spaces) => {
+					if (prefix && language !== 'diff') return match;
+
+					// for no good reason at all, marked replaces tabs with spaces
+					let tabs = '';
+					for (let i = 0; i < spaces.length; i += 4) {
+						tabs += '  ';
+					}
+					return prefix + tabs;
+				})
+				.replace(/\*\\\//g, '*/');
+
+			let version_class = '';
+			if (language === 'generated-ts' || language === 'generated-svelte') {
+				language = language.replace('generated-', '');
+				version_class = 'ts-version';
+			} else if (language === 'original-js' || language === 'original-svelte') {
+				language = language.replace('original-', '');
+				version_class = 'js-version';
+			}
+
+			// TODO: Replace later
+			html = highlighter.codeToHtml(source, { lang: languages[language] });
+
+			// if (language === 'dts') {
+			// 	// @ts-ignore
+			// 	html = renderCodeToHTML(source, 'ts', { twoslash: false }, {}, highlighter);
+			// } else if (language === 'js' || language === 'ts') {
+			// 	try {
+			// 		const injected = [];
+			// 		if (
+			// 			source.includes('$app/') ||
+			// 			source.includes('$service-worker') ||
+			// 			source.includes('@sveltejs/kit/')
+			// 		) {
+			// 			injected.push(
+			// 				`// @filename: ambient-kit.d.ts`,
+			// 				`/// <reference types="@sveltejs/kit" />`
+			// 			);
+			// 		}
+
+			// 		if (source.includes('$env/')) {
+			// 			// TODO we're hardcoding static env vars that are used in code examples
+			// 			// in the types, which isn't... totally ideal, but will do for now
+			// 			injected.push(
+			// 				`declare module '$env/dynamic/private' { export const env: Record<string, string> }`,
+			// 				`declare module '$env/dynamic/public' { export const env: Record<string, string> }`,
+			// 				`declare module '$env/static/private' { export const API_KEY: string }`,
+			// 				`declare module '$env/static/public' { export const PUBLIC_BASE_URL: string }`
+			// 			);
+			// 		}
+
+			// 		if (source.includes('./$types') && !source.includes('@filename: $types.d.ts')) {
+			// 			const params = parse_route_id(options.file || `+page.${language}`)
+			// 				.params.map((param) => `${param.name}: string`)
+			// 				.join(', ');
+
+			// 			injected.push(
+			// 				`// @filename: $types.d.ts`,
+			// 				`import type * as Kit from '@sveltejs/kit';`,
+			// 				`export type PageLoad = Kit.Load<{${params}}>;`,
+			// 				`export type PageServerLoad = Kit.ServerLoad<{${params}}>;`,
+			// 				`export type LayoutLoad = Kit.Load<{${params}}>;`,
+			// 				`export type LayoutServerLoad = Kit.ServerLoad<{${params}}>;`,
+			// 				`export type RequestHandler = Kit.RequestHandler<{${params}}>;`,
+			// 				`export type Action = Kit.Action<{${params}}>;`,
+			// 				`export type Actions = Kit.Actions<{${params}}>;`
+			// 			);
+			// 		}
+
+			// 		// special case — we need to make allowances for code snippets coming
+			// 		// from e.g. ambient.d.ts
+			// 		if (file.endsWith('30-modules.md')) {
+			// 			injected.push('// @errors: 7006 7031');
+			// 		}
+
+			// 		// another special case
+			// 		if (source.includes('$lib/types')) {
+			// 			injected.push(`declare module '$lib/types' { export interface User {} }`);
+			// 		}
+
+			// 		if (injected.length) {
+			// 			const injected_str = injected.join('\n');
+			// 			if (source.includes('// @filename:')) {
+			// 				source = source.replace('// @filename:', `${injected_str}\n\n// @filename:`);
+			// 			} else {
+			// 				source = source.replace(
+			// 					/^(?!\/\/ @)/m,
+			// 					`${injected_str}\n\n// @filename: index.${language}\n// ---cut---\n`
+			// 				);
+			// 			}
+			// 		}
+
+			// 		const twoslash = runTwoSlash(source, language, {
+			// 			defaultCompilerOptions: {
+			// 				allowJs: true,
+			// 				checkJs: true,
+			// 				target: 'es2021',
+			// 			},
+			// 		});
+
+			// 		html = renderCodeToHTML(
+			// 			twoslash.code,
+			// 			'ts',
+			// 			{ twoslash: true },
+			// 			{},
+			// 			highlighter,
+			// 			twoslash
+			// 		);
+			// 	} catch (e) {
+			// 		console.error(`Error compiling snippet in ${file}`);
+			// 		console.error(e.code);
+			// 		throw e;
+			// 	}
+
+			// 	// we need to be able to inject the LSP attributes as HTML, not text, so we
+			// 	// turn &lt; into &amp;lt;
+			// 	html = html.replace(
+			// 		/<data-lsp lsp='([^']*)'([^>]*)>(\w+)<\/data-lsp>/g,
+			// 		(match, lsp, attrs, name) => {
+			// 			if (!lsp) return name;
+			// 			return `<data-lsp lsp='${lsp.replace(/&/g, '&amp;')}'${attrs}>${name}</data-lsp>`;
+			// 		}
+			// 	);
+
+			// 	// preserve blank lines in output (maybe there's a more correct way to do this?)
+			// 	html = html.replace(/<div class='line'><\/div>/g, '<div class="line"> </div>');
+			// } else if (language === 'diff') {
+			// 	const lines = source.split('\n').map((content) => {
+			// 		let type = null;
+			// 		if (/^[\+\-]/.test(content)) {
+			// 			type = content[0] === '+' ? 'inserted' : 'deleted';
+			// 			content = content.slice(1);
+			// 		}
+
+			// 		return {
+			// 			type,
+			// 			content: escape(content),
+			// 		};
+			// 	});
+
+			// 	html = `<pre class="language-diff"><code>${lines
+			// 		.map((line) => {
+			// 			if (line.type) return `<span class="${line.type}">${line.content}\n</span>`;
+			// 			return line.content + '\n';
+			// 		})
+			// 		.join('')}</code></pre>`;
+			// } else {
+			// 	const plang = languages[language];
+			// 	const highlighted = plang
+			// 		? PrismJS.highlight(source, PrismJS.languages[plang], language)
+			// 		: source.replace(/[&<>]/g, (c) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;' }[c]));
+
+			// 	html = `<pre class='language-${plang}'><code>${highlighted}</code></pre>`;
+			// }
+
+			if (options.file) {
+				html = `<div class="code-block"><span class="filename">${options.file}</span>${html}</div>`;
+			}
+
+			if (version_class) {
+				html = html.replace(/class=('|")/, `class=$1${version_class} `);
+			}
+
+			// type_regex.lastIndex = 0;
+
+			html = html
+				// .replace(type_regex, (match, prefix, name) => {
+				// 	if (options.link === 'false' || name === current) {
+				// 		// we don't want e.g. RequestHandler to link to RequestHandler
+				// 		return match;
+				// 	}
+
+				// 	const link = `<a href="${type_links.get(name)}">${name}</a>`;
+				// 	return `${prefix || ''}${link}`;
+				// })
+				.replace(
+					/^(\s+)<span class="token comment">([\s\S]+?)<\/span>\n/gm,
+					(match, intro_whitespace, content) => {
+						// we use some CSS trickery to make comments break onto multiple lines while preserving indentation
+						const lines = (intro_whitespace + content).split('\n');
+						return lines
+							.map((line) => {
+								const match = /^(\s*)(.*)/.exec(line);
+								const indent = (match[1] ?? '').replace(/\t/g, '  ').length;
+
+								return `<span class="token comment wrapped" style="--indent: ${indent}ch">${
+									line ?? ''
+								}</span>`;
+							})
+							.join('');
+					}
+				)
+				.replace(/\/\*…\*\//g, '…');
+
+			// fs.writeFileSync(`${snippet_cache}/${digest}.html`, html);
+			return html;
+		},
+		codespan: (text) => {
+			return (
+				'<code>' +
+				text +
+				// text.replace(type_regex, (match, prefix, name) => {
+				// 	const link = `<a href="${type_links.get(name)}">${name}</a>`;
+				// 	return `${prefix || ''}${link}`;
+				// }) +
+				'</code>'
+			);
+		},
+	});
+
+	return {
+		file,
+		slug: match[1],
+		title: metadata.title,
+		content,
+		sections,
+	};
+}
+
+/** @param {string} title */
+export function slugify(title) {
+	return title
+		.toLowerCase()
+		.replace(/&lt;/g, '')
+		.replace(/&gt;/g, '')
+		.replace(/[^a-z0-9-$]/g, '-')
+		.replace(/-{2,}/g, '-')
+		.replace(/^-/, '')
+		.replace(/-$/, '');
+}
+
+/**
+ * @param {{
+ *   file: string;
+ *   body: string;
+ *   code: (source: string, language: string, current: string) => string;
+ *   codespan: (source: string) => string;
+ * }} opts
+ */
+function parse({ file, body, code, codespan }) {
+	const headings = [];
+
+	/** @type {import('./types').Section[]} */
+	const sections = [];
+
+	/** @type {import('./types').Section} */
+	let section;
+
+	// this is a bit hacky, but it allows us to prevent type declarations
+	// from linking to themselves
+	let current = '';
+
+	/** @type {string} */
+	const content = transform(body, {
+		/**
+		 * @param {string} html
+		 * @param {number} level
+		 */
+		heading(html, level) {
+			const title = html
+				.replace(/<\/?code>/g, '')
+				.replace(/&quot;/g, '"')
+				.replace(/&lt;/g, '<')
+				.replace(/&gt;/g, '>');
+
+			current = title;
+
+			const normalized = slugify(title);
+
+			headings[level - 1] = normalized;
+			headings.length = level;
+
+			const slug = headings.filter(Boolean).join('-');
+
+			// TODO: All this will need to change when headings in documentation are restructured to be h2+ instead of h3+ as they are right now
+			if (level === 3) {
+				section = {
+					title,
+					slug,
+					sections: [],
+				};
+
+				sections.push(section);
+			} else if (level === 4 || level === 5) {
+				(section?.sections ?? sections).push({
+					title,
+					slug,
+				});
+			} else {
+				throw new Error(`Unexpected <h${level}> in ${file}`);
+			}
+
+			return `<h${
+				level - 1
+			} id="${slug}">${html}<a href="#${slug}" class="permalink"><span class="visually-hidden">permalink</span></a></h${level}>`;
+		},
+		code: (source, language) => code(source, language, current),
+		codespan,
+	});
+
+	return {
+		sections,
+		content,
+	};
+}
+
+export function generate_ts_from_js(markdown) {
+	return markdown
+		.replaceAll(/```js\n([\s\S]+?)\n```/g, (match, code) => {
+			if (!code.includes('/// file:')) {
+				// No named file -> assume that the code is not meant to be shown in two versions
+				return match;
+			}
+			if (code.includes('/// file: svelte.config.js')) {
+				// svelte.config.js has no TS equivalent
+				return match;
+			}
+
+			const ts = convert_to_ts(code);
+
+			if (!ts) {
+				// No changes -> don't show TS version
+				return match;
+			}
+
+			return match.replace('js', 'original-js') + '\n```generated-ts\n' + ts + '\n```';
+		})
+		.replaceAll(/```svelte\n([\s\S]+?)\n```/g, (match, code) => {
+			if (!code.includes('/// file:')) {
+				// No named file -> assume that the code is not meant to be shown in two versions
+				return match;
+			}
+
+			// Assumption: no context="module" blocks
+			const script = code.match(/<script>([\s\S]+?)<\/script>/);
+			if (!script) return match;
+
+			const [outer, inner] = script;
+			const ts = convert_to_ts(inner, '\t', '\n');
+
+			if (!ts) {
+				// No changes -> don't show TS version
+				return match;
+			}
+
+			return (
+				match.replace('svelte', 'original-svelte') +
+				'\n```generated-svelte\n' +
+				code.replace(outer, `<script lang="ts">${ts}</script>`) +
+				'\n```'
+			);
+		});
+}
+
+/**
+ * Transforms a JS code block into a TS code block by turning JSDoc into type annotations.
+ * Due to pragmatism only the cases currently used in the docs are implemented.
+ * @param {string} js_code
+ * @param {string} [indent]
+ * @param {string} [offset]
+ *  */
+function convert_to_ts(js_code, indent = '', offset = '') {
+	js_code = js_code
+		.replaceAll('// @filename: index.js', '// @filename: index.ts')
+		.replace(/(\/\/\/ .+?\.)js/, '$1ts')
+		// *\/ appears in some JsDoc comments in d.ts files due to the JSDoc-in-JSDoc problem
+		.replace(/\*\\\//g, '*/');
+
+	const ast = ts.createSourceFile(
+		'filename.ts',
+		js_code,
+		ts.ScriptTarget.Latest,
+		true,
+		ts.ScriptKind.TS
+	);
+	const code = new MagicString(js_code);
+	const imports = new Map();
+
+	function walk(node) {
+		// @ts-ignore
+		if (node.jsDoc) {
+			// @ts-ignore
+			for (const comment of node.jsDoc) {
+				let modified = false;
+
+				for (const tag of comment.tags ?? []) {
+					if (ts.isJSDocTypeTag(tag)) {
+						const [name, generics] = get_type_info(tag);
+
+						if (ts.isFunctionDeclaration(node)) {
+							const is_export = node.modifiers?.some(
+								(modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword
+							)
+								? 'export '
+								: '';
+							const is_async = node.modifiers?.some(
+								(modifier) => modifier.kind === ts.SyntaxKind.AsyncKeyword
+							);
+							code.overwrite(
+								node.getStart(),
+								node.name.getEnd(),
+								`${is_export ? 'export ' : ''}const ${node.name.getText()} = (${
+									is_async ? 'async ' : ''
+								}`
+							);
+							code.appendLeft(node.body.getStart(), '=> ');
+							const type = generics !== undefined ? `${name}${generics}` : name;
+							code.appendLeft(node.body.getEnd(), `) satisfies ${type};`);
+
+							modified = true;
+						} else if (
+							ts.isVariableStatement(node) &&
+							node.declarationList.declarations.length === 1
+						) {
+							const variable_statement = node.declarationList.declarations[0];
+
+							if (variable_statement.name.getText() === 'actions') {
+								code.appendLeft(variable_statement.getEnd(), ` satisfies ${name}`);
+							} else {
+								code.appendLeft(variable_statement.name.getEnd(), `: ${name}`);
+							}
+
+							modified = true;
+						} else {
+							throw new Error('Unhandled @type JsDoc->TS conversion: ' + js_code);
+						}
+					} else if (ts.isJSDocParameterTag(tag) && ts.isFunctionDeclaration(node)) {
+						if (node.parameters.length !== 1) {
+							throw new Error(
+								'Unhandled @type JsDoc->TS conversion; needs more params logic: ' + node.getText()
+							);
+						}
+						const [name] = get_type_info(tag);
+						code.appendLeft(node.parameters[0].getEnd(), `: ${name}`);
+
+						modified = true;
+					}
+				}
+
+				if (modified) {
+					code.overwrite(comment.getStart(), comment.getEnd(), '');
+				}
+			}
+		}
+
+		ts.forEachChild(node, walk);
+	}
+
+	walk(ast);
+
+	if (imports.size) {
+		const import_statements = Array.from(imports.entries())
+			.map(([from, names]) => {
+				return `${indent}import type { ${Array.from(names).join(', ')} } from '${from}';`;
+			})
+			.join('\n');
+		const idxOfLastImport = [...ast.statements]
+			.reverse()
+			.find((statement) => ts.isImportDeclaration(statement))
+			?.getEnd();
+		const insertion_point = Math.max(
+			idxOfLastImport ? idxOfLastImport + 1 : 0,
+			js_code.includes('---cut---')
+				? js_code.indexOf('\n', js_code.indexOf('---cut---')) + 1
+				: js_code.includes('/// file:')
+				? js_code.indexOf('\n', js_code.indexOf('/// file:')) + 1
+				: 0
+		);
+		code.appendLeft(insertion_point, offset + import_statements + '\n');
+	}
+
+	const transformed = code.toString();
+	return transformed === js_code ? undefined : transformed.replace(/\n\s*\n\s*\n/g, '\n\n');
+
+	/** @param {ts.JSDocTypeTag | ts.JSDocParameterTag} tag */
+	function get_type_info(tag) {
+		const type_text = tag.typeExpression.getText();
+		let name = type_text.slice(1, -1); // remove { }
+
+		const import_match = /import\('(.+?)'\)\.(\w+)(<{?[\n\* \w:;,]+}?>)?/.exec(type_text);
+		if (import_match) {
+			const [, from, _name, generics] = import_match;
+			name = _name;
+			const existing = imports.get(from);
+			if (existing) {
+				existing.add(name);
+			} else {
+				imports.set(from, new Set([name]));
+			}
+			if (generics !== undefined) {
+				return [
+					name,
+					generics
+						.replaceAll('*', '') // get rid of JSDoc asterisks
+						.replace('  }>', '}>'), // unindent closing brace
+				];
+			}
+		}
+		return [name];
+	}
+}
diff --git a/sites/svelte.dev/src/lib/server/docs/types.ts b/sites/svelte.dev/src/lib/server/docs/types.ts
new file mode 100644
index 0000000000..c627e1b68b
--- /dev/null
+++ b/sites/svelte.dev/src/lib/server/docs/types.ts
@@ -0,0 +1,13 @@
+export interface Section {
+	title: string;
+	slug: string;
+	sections?: Section[];
+}
+
+export interface Type {
+	name: string;
+	comment: string;
+	snippet: string;
+	bullets: string[];
+	children: Type[];
+}
diff --git a/sites/svelte.dev/src/lib/server/markdown/index.js b/sites/svelte.dev/src/lib/server/markdown/index.js
index d708622c9f..a5e9aa0b79 100644
--- a/sites/svelte.dev/src/lib/server/markdown/index.js
+++ b/sites/svelte.dev/src/lib/server/markdown/index.js
@@ -1,74 +1,181 @@
-import fs from 'fs';
-import { transform } from './marked';
+import { marked } from 'marked';
+
+const escapeTest = /[&<>"']/;
+const escapeReplace = /[&<>"']/g;
+const escapeTestNoEncode = /[<>"']|&(?!#?\w+;)/;
+const escapeReplaceNoEncode = /[<>"']|&(?!#?\w+;)/g;
+const escapeReplacements = {
+	'&': '&amp;',
+	'<': '&lt;',
+	'>': '&gt;',
+	'"': '&quot;',
+	"'": '&#39;',
+};
 
 /**
- * @returns {import('./types').BlogPostSummary[]}
+ * @param {keyof typeof escapeReplacements} ch
  */
-export function get_index() {
-	return fs
-		.readdirSync('content/blog')
-		.reverse()
-		.map((file) => {
-			if (!file.endsWith('.md')) return;
-
-			const { date, slug } = get_date_and_slug(file);
-
-			const content = fs.readFileSync(`content/blog/${file}`, 'utf-8');
-			const { metadata } = extract_frontmatter(content);
-
-			return {
-				slug,
-				date,
-				title: metadata.title,
-				description: metadata.description,
-				draft: !!metadata.draft
-			};
-		});
-}
+const getEscapeReplacement = (ch) => escapeReplacements[ch];
 
 /**
- * @param {string} slug
- * @returns {import('./types').BlogPost}
+ * @param {string} html
+ * @param {boolean} encode
  */
-export function get_post(slug) {
-	for (const file of fs.readdirSync('content/blog')) {
-		if (!file.endsWith('.md')) continue;
-		if (file.slice(11, -3) !== slug) continue;
-
-		const { date, date_formatted } = get_date_and_slug(file);
-
-		const content = fs.readFileSync(`content/blog/${file}`, 'utf-8');
-		const { metadata, body } = extract_frontmatter(content);
-
-		return {
-			date,
-			date_formatted,
-			title: metadata.title,
-			description: metadata.description,
-			author: {
-				name: metadata.author,
-				url: metadata.authorURL
-			},
-			draft: !!metadata.draft,
-			content: transform(body)
-		};
+export function escape(html, encode = false) {
+	if (encode) {
+		if (escapeTest.test(html)) {
+			return html.replace(escapeReplace, getEscapeReplacement);
+		}
+	} else {
+		if (escapeTestNoEncode.test(html)) {
+			return html.replace(escapeReplaceNoEncode, getEscapeReplacement);
+		}
 	}
+
+	return html;
 }
 
-/** @param {string} filename */
-function get_date_and_slug(filename) {
-	const match = /^(\d{4}-\d{2}-\d{2})-(.+)\.md$/.exec(filename);
-	if (!match) throw new Error(`Invalid filename for blog: '${filename}'`);
+/** @type {Partial<import('marked').Renderer>} */
+const default_renderer = {
+	code(code, infostring, escaped) {
+		const lang = (infostring || '').match(/\S*/)[0];
+
+		code = code.replace(/\n$/, '') + '\n';
+
+		if (!lang) {
+			return '<pre><code>' + (escaped ? code : escape(code, true)) + '</code></pre>\n';
+		}
+
+		return (
+			'<pre><code class="language-' +
+			escape(lang, true) +
+			'">' +
+			(escaped ? code : escape(code, true)) +
+			'</code></pre>\n'
+		);
+	},
+
+	blockquote(quote) {
+		return '<blockquote>\n' + quote + '</blockquote>\n';
+	},
+
+	html(html) {
+		return html;
+	},
+
+	heading(text, level) {
+		return '<h' + level + '>' + text + '</h' + level + '>\n';
+	},
+
+	hr() {
+		return '<hr>\n';
+	},
+
+	list(body, ordered, start) {
+		const type = ordered ? 'ol' : 'ul',
+			startatt = ordered && start !== 1 ? ' start="' + start + '"' : '';
+		return '<' + type + startatt + '>\n' + body + '</' + type + '>\n';
+	},
+
+	listitem(text) {
+		return '<li>' + text + '</li>\n';
+	},
+
+	checkbox(checked) {
+		return '<input ' + (checked ? 'checked="" ' : '') + 'disabled="" type="checkbox"' + '' + '> ';
+	},
+
+	paragraph(text) {
+		return '<p>' + text + '</p>\n';
+	},
+
+	table(header, body) {
+		if (body) body = '<tbody>' + body + '</tbody>';
+
+		return '<table>\n' + '<thead>\n' + header + '</thead>\n' + body + '</table>\n';
+	},
+
+	tablerow(content) {
+		return '<tr>\n' + content + '</tr>\n';
+	},
+
+	tablecell(content, flags) {
+		const type = flags.header ? 'th' : 'td';
+		const tag = flags.align ? '<' + type + ' align="' + flags.align + '">' : '<' + type + '>';
+		return tag + content + '</' + type + '>\n';
+	},
+
+	// span level renderer
+	strong(text) {
+		return '<strong>' + text + '</strong>';
+	},
+
+	em(text) {
+		return '<em>' + text + '</em>';
+	},
+
+	codespan(text) {
+		return '<code>' + text + '</code>';
+	},
+
+	br() {
+		return '<br>';
+	},
+
+	del(text) {
+		return '<del>' + text + '</del>';
+	},
+
+	link(href, title, text) {
+		if (href === null) {
+			return text;
+		}
+		let out = '<a href="' + escape(href) + '"';
+		if (title) {
+			out += ' title="' + title + '"';
+		}
+		out += '>' + text + '</a>';
+		return out;
+	},
+
+	image(href, title, text) {
+		if (href === null) {
+			return text;
+		}
+
+		let out = '<img src="' + href + '" alt="' + text + '"';
+		if (title) {
+			out += ' title="' + title + '"';
+		}
+		out += '>';
+		return out;
+	},
+
+	text(text) {
+		return text;
+	},
+};
 
-	const [, date, slug] = match;
-	const [y, m, d] = date.split('-');
-	const date_formatted = `${months[+m - 1]} ${+d} ${y}`;
+/**
+ * @param {string} markdown
+ * @param {Partial<import('marked').Renderer>} renderer
+ */
+export function transform(markdown, renderer = {}) {
+	marked.use({
+		renderer: {
+			// we have to jump through these hoops because of marked's API design choices —
+			// options are global, and merged in confusing ways. You can't do e.g.
+			// `new Marked(options).parse(markdown)`
+			...default_renderer,
+			...renderer,
+		},
+	});
 
-	return { date, date_formatted, slug };
+	return marked(markdown);
 }
 
 /** @param {string} markdown */
-function extract_frontmatter(markdown) {
+export function extract_frontmatter(markdown) {
 	const match = /---\r?\n([\s\S]+?)\r?\n---/.exec(markdown);
 	const frontmatter = match[1];
 	const body = markdown.slice(match[0].length);
@@ -77,17 +184,8 @@ function extract_frontmatter(markdown) {
 	const metadata = {};
 	frontmatter.split('\n').forEach((pair) => {
 		const i = pair.indexOf(':');
-		metadata[pair.slice(0, i).trim()] = strip_quotes(pair.slice(i + 1).trim());
+		metadata[pair.slice(0, i).trim()] = pair.slice(i + 1).trim();
 	});
 
 	return { metadata, body };
 }
-
-function strip_quotes(str) {
-	if (str[0] === '"' && str[str.length - 1] === '"') return str.slice(1, -1);
-	return str;
-}
-
-const months = 'Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec'.split(' ');
-
-function format_date(date) {}
diff --git a/sites/svelte.dev/src/routes/+layout.svelte b/sites/svelte.dev/src/routes/+layout.svelte
index 70849596bb..6d5c9da359 100644
--- a/sites/svelte.dev/src/routes/+layout.svelte
+++ b/sites/svelte.dev/src/routes/+layout.svelte
@@ -1,9 +1,8 @@
 <script>
-	import "@sveltejs/site-kit/styles/index.css";
-	import { page, navigating } from "$app/stores";
-	import { Icon, Icons, Nav, NavItem, SkipLink } from "@sveltejs/site-kit";
-	import PreloadingIndicator from "$lib/components/PreloadingIndicator.svelte";
-	import StopWar from "./stopwar.svg";
+	import { navigating, page } from '$app/stores';
+	import PreloadingIndicator from '$lib/components/PreloadingIndicator.svelte';
+	import { Icon, Icons, Nav, NavItem, SkipLink } from '@sveltejs/site-kit';
+	import '@sveltejs/site-kit/styles/index.css';
 </script>
 
 <Icons />
@@ -12,9 +11,9 @@
 	<PreloadingIndicator />
 {/if}
 
-{#if $page.url.pathname !== "/repl/embed"}
+{#if $page.url.pathname !== '/repl/embed'}
 	<SkipLink href="#main" />
-	<Nav {page} logo={StopWar}>
+	<Nav {page} logo="/svelte-logo.svg">
 		<svelte:fragment slot="nav-center">
 			<NavItem href="/tutorial">Tutorial</NavItem>
 			<NavItem href="/docs">Docs</NavItem>
@@ -32,10 +31,7 @@
 				<span class="large"><Icon name="message-square" /></span>
 			</NavItem>
 
-			<NavItem
-				external="https://github.com/sveltejs/svelte"
-				title="GitHub Repo"
-			>
+			<NavItem external="https://github.com/sveltejs/svelte" title="GitHub Repo">
 				<span class="small">GitHub</span>
 				<span class="large"><Icon name="github" /></span>
 			</NavItem>
@@ -44,16 +40,10 @@
 {/if}
 
 <svelte:head>
-	{#if $page.route.id !== "/blog/[slug]"}
+	{#if $page.route.id !== '/blog/[slug]'}
 		<meta name="twitter:card" content="summary" />
-		<meta
-			name="twitter:image"
-			content="https://svelte.dev/images/twitter-thumbnail.jpg"
-		/>
-		<meta
-			name="og:image"
-			content="https://svelte.dev/images/twitter-thumbnail.jpg"
-		/>
+		<meta name="twitter:image" content="https://svelte.dev/images/twitter-thumbnail.jpg" />
+		<meta name="og:image" content="https://svelte.dev/images/twitter-thumbnail.jpg" />
 	{/if}
 </svelte:head>
 
@@ -61,29 +51,7 @@
 	<slot />
 </main>
 
-<a target="_blank" rel="noopener noreferrer" href="https://www.stopputin.net/">
-	<div class="ukr">
-		<span class="small">
-			<strong>We stand with Ukraine.</strong> Donate →
-		</span>
-		<span class="large">
-			<strong>We stand with Ukraine.</strong> Petition your leaders. Show your support.
-		</span>
-	</div>
-</a>
-
 <style>
-	.ukr {
-		background-color: #0066cc;
-		color: white;
-		position: fixed;
-		bottom: 0;
-		width: 100vw;
-		text-align: center;
-		padding: 0.75em;
-		z-index: 999;
-	}
-
 	@media (max-width: 830px) {
 		:global(aside) {
 			z-index: 9999 !important;
@@ -125,19 +93,6 @@
 		padding-bottom: var(--ukr-footer-height);
 	}
 
-	.ukr {
-		background-color: #0066cc;
-		color: white;
-		position: fixed;
-		display: flex;
-		align-items: center;
-		justify-content: center;
-		bottom: 0;
-		width: 100vw;
-		height: var(--ukr-footer-height);
-		z-index: 999;
-	}
-
 	:global(.examples-container, .repl-outer, .tutorial-outer) {
 		height: calc(100vh - var(--nav-h) - var(--ukr-footer-height)) !important;
 	}
@@ -155,7 +110,4 @@
 			z-index: 9999 !important;
 		}
 	}
-	.ukr strong {
-		color: #ffcc00;
-	}
 </style>
diff --git a/sites/svelte.dev/src/routes/_components/WhosUsingSvelte/index.svelte b/sites/svelte.dev/src/routes/_components/WhosUsingSvelte/index.svelte
index dcc6e09570..3d05bcd4b9 100644
--- a/sites/svelte.dev/src/routes/_components/WhosUsingSvelte/index.svelte
+++ b/sites/svelte.dev/src/routes/_components/WhosUsingSvelte/index.svelte
@@ -6,7 +6,7 @@
 
 <div class="logos">
 	{#each sorted as { href, filename, alt, style, invert, width, height }}
-		<a target="_blank" rel="noopener" {href} class:invert style={style || ''}>
+		<a target="_blank" rel="noreferrer" {href} class:invert style={style || ''}>
 			<img src="/whos-using-svelte/{filename}" {alt} {width} {height} loading="lazy" />
 		</a>
 
diff --git a/sites/svelte.dev/src/routes/blog/+page.server.js b/sites/svelte.dev/src/routes/blog/+page.server.js
index 4a6091a9f5..0a1ca27d9f 100644
--- a/sites/svelte.dev/src/routes/blog/+page.server.js
+++ b/sites/svelte.dev/src/routes/blog/+page.server.js
@@ -1,9 +1,9 @@
-import { get_index } from '$lib/server/markdown';
+import { get_index } from '$lib/server/blog';
 
 export const prerender = true;
 
 export async function load() {
 	return {
-		posts: get_index()
+		posts: get_index(),
 	};
 }
diff --git a/sites/svelte.dev/src/routes/blog/[slug]/+page.server.js b/sites/svelte.dev/src/routes/blog/[slug]/+page.server.js
index 3f102cbf95..57005879b9 100644
--- a/sites/svelte.dev/src/routes/blog/[slug]/+page.server.js
+++ b/sites/svelte.dev/src/routes/blog/[slug]/+page.server.js
@@ -1,4 +1,4 @@
-import { get_post } from '$lib/server/markdown/index.js';
+import { get_post } from '$lib/server/blog/index.js';
 import { error } from '@sveltejs/kit';
 
 export const prerender = true;
@@ -11,6 +11,6 @@ export async function load({ params }) {
 	}
 
 	return {
-		post
+		post,
 	};
 }
diff --git a/sites/svelte.dev/src/routes/blog/[slug]/card.png/+server.js b/sites/svelte.dev/src/routes/blog/[slug]/card.png/+server.js
index f199d24046..a63103765c 100644
--- a/sites/svelte.dev/src/routes/blog/[slug]/card.png/+server.js
+++ b/sites/svelte.dev/src/routes/blog/[slug]/card.png/+server.js
@@ -2,7 +2,7 @@ import satori from 'satori';
 import { Resvg } from '@resvg/resvg-js';
 import OverpassRegular from './Overpass-Regular.ttf';
 import { html as toReactNode } from 'satori-html';
-import { get_post } from '$lib/server/markdown/index.js';
+import { get_post } from '$lib/server/blog/index.js';
 import { error } from '@sveltejs/kit';
 import Card from './Card.svelte';
 
@@ -19,6 +19,7 @@ export const GET = async ({ params, url }) => {
 		throw error(404);
 	}
 
+	// @ts-ignore
 	const result = Card.render({ post });
 	const element = toReactNode(`${result.html}<style>${result.css.code}</style>`);
 
@@ -28,18 +29,18 @@ export const GET = async ({ params, url }) => {
 				name: 'Overpass',
 				data: Buffer.from(OverpassRegular),
 				style: 'normal',
-				weight: 400
-			}
+				weight: 400,
+			},
 		],
 		height,
-		width
+		width,
 	});
 
 	const resvg = new Resvg(svg, {
 		fitTo: {
 			mode: 'width',
-			value: width
-		}
+			value: width,
+		},
 	});
 
 	const image = resvg.render();
@@ -47,7 +48,7 @@ export const GET = async ({ params, url }) => {
 	return new Response(image.asPng(), {
 		headers: {
 			'content-type': 'image/png',
-			'cache-control': 'public, max-age=600' // cache for 10 minutes
-		}
+			'cache-control': 'public, max-age=600', // cache for 10 minutes
+		},
 	});
 };
diff --git a/sites/svelte.dev/src/routes/blog/rss.xml/+server.js b/sites/svelte.dev/src/routes/blog/rss.xml/+server.js
index 2c9af085e8..353c634d75 100644
--- a/sites/svelte.dev/src/routes/blog/rss.xml/+server.js
+++ b/sites/svelte.dev/src/routes/blog/rss.xml/+server.js
@@ -1,4 +1,4 @@
-import { get_index } from '$lib/server/markdown';
+import { get_index } from '$lib/server/blog';
 
 export const prerender = true;
 
@@ -15,13 +15,13 @@ function escapeHTML(html) {
 		"'": '#39',
 		'&': 'amp',
 		'<': 'lt',
-		'>': 'gt'
+		'>': 'gt',
 	};
 
 	return html.replace(/["'&<>]/g, (c) => `&${chars[c]};`);
 }
 
-/** @type {import('$lib/server/markdown/types').BlogPostSummary[]} */
+/** @param {import('$lib/server/blog/types').BlogPostSummary[]} posts */
 const get_rss = (posts) =>
 	`
 <?xml version="1.0" encoding="UTF-8" ?>
@@ -63,7 +63,7 @@ export async function GET() {
 	return new Response(get_rss(posts), {
 		headers: {
 			'Cache-Control': `max-age=${30 * 60 * 1e3}`,
-			'Content-Type': 'application/rss+xml'
-		}
+			'Content-Type': 'application/rss+xml',
+		},
 	});
 }
diff --git a/sites/svelte.dev/src/routes/docs/+layout.server.js b/sites/svelte.dev/src/routes/docs/+layout.server.js
new file mode 100644
index 0000000000..222f0b3065
--- /dev/null
+++ b/sites/svelte.dev/src/routes/docs/+layout.server.js
@@ -0,0 +1,23 @@
+import { extract_frontmatter } from '$lib/server/markdown';
+import fs from 'fs';
+import { base } from '$app/paths';
+
+export const prerender = true;
+
+const base_dir = '../../site/content/docs/';
+
+/** @type {import('./$types').LayoutServerLoad} */
+export function load() {
+	const sections = fs.readdirSync(base_dir).map((file) => {
+		const { title } = extract_frontmatter(fs.readFileSync(`${base_dir}/${file}`, 'utf-8')).metadata;
+
+		return {
+			title,
+			path: `${base}/docs/${file.slice(3, -3)}`,
+		};
+	});
+
+	return {
+		sections,
+	};
+}
diff --git a/sites/svelte.dev/src/routes/docs/+layout.svelte b/sites/svelte.dev/src/routes/docs/+layout.svelte
new file mode 100644
index 0000000000..4434fb7698
--- /dev/null
+++ b/sites/svelte.dev/src/routes/docs/+layout.svelte
@@ -0,0 +1,337 @@
+<script>
+	import { page } from '$app/stores';
+	import Contents from './Contents.svelte';
+	import '@sveltejs/site-kit/styles/code.css';
+
+	/** @type {import('./$types').LayoutServerData}*/
+	export let data;
+</script>
+
+<div class="container">
+	<div class="page content">
+		<h1>{data.sections.find((val) => val.path === $page.url.pathname)?.title}</h1>
+		<slot />
+	</div>
+
+	<div class="toc-container">
+		<Contents contents={data.sections} />
+	</div>
+</div>
+
+<style>
+	.container {
+		--sidebar-menu-width: 20rem;
+		--sidebar-width: var(--sidebar-menu-width);
+		--ts-toggle-height: 4.2rem;
+	}
+
+	.page {
+		--on-this-page-display: none;
+		padding: var(--sk-page-padding-top) var(--sk-page-padding-side);
+	}
+
+	.page :global(hr) {
+		display: none;
+	}
+
+	.content {
+		width: 100%;
+		margin: 0;
+		padding: var(--sk-page-padding-top) var(--sk-page-padding-side);
+		tab-size: 2;
+		-moz-tab-size: 2;
+	}
+
+	@media (min-width: 832px) {
+		/* can't use vars in @media :( */
+		.content {
+			padding-left: calc(var(--sidebar-width) + var(--sk-page-padding-side));
+		}
+	}
+
+	.content :global(h1) {
+		font-size: 3.2rem;
+		margin: 0 0 0.5em 0;
+	}
+
+	.content :global(h2) {
+		margin-top: 8rem;
+		padding: 2rem 1.6rem 2rem 0.2rem;
+		border-bottom: 1px solid hsl(0, 0%, 87%, 0.2);
+		line-height: 1;
+		font-size: var(--sk-text-m);
+		letter-spacing: 0.05em;
+		text-transform: uppercase;
+	}
+
+	.content :global(section):first-of-type > :global(h2) {
+		margin-top: 0;
+	}
+
+	.content :global(h4) {
+		margin: 2em 0 1em 0;
+	}
+
+	.content :global(.offset-anchor) {
+		position: relative;
+		display: block;
+		top: calc(-1 * var(--sk-page-padding-top));
+		width: 0;
+		height: 0;
+	}
+
+	.content :global(.anchor) {
+		position: absolute;
+		display: block;
+		background: url(../icons/link.svg) 0 50% no-repeat;
+		background-size: 1em 1em;
+		width: 1.4em;
+		height: 1em;
+		left: -1.3em;
+		bottom: 0.3rem;
+		opacity: 0;
+		transition: opacity 0.2s;
+		user-select: none;
+	}
+
+	.content :global(h2) :global(.anchor) {
+		bottom: 4rem;
+	}
+
+	.content :global(h3) :global(.anchor) {
+		bottom: 1rem;
+	}
+
+	@media (min-width: 400px) {
+		.content :global(h1) {
+			font-size: 4.2rem;
+		}
+	}
+
+	@media (min-width: 768px) {
+		.content :global(h1) {
+			font-size: 5.4rem;
+		}
+
+		.content :global(.anchor:focus),
+		.content :global(h2):hover :global(.anchor),
+		.content :global(h3):hover :global(.anchor),
+		.content :global(h4):hover :global(.anchor),
+		.content :global(h5):hover :global(.anchor),
+		.content :global(h6):hover :global(.anchor) {
+			opacity: 1;
+		}
+	}
+
+	.content :global(h3),
+	.content :global(h3 > code) {
+		margin: 6.4rem 0 1rem 0;
+		padding: 0 0 1rem 0;
+		color: var(--sk-text-2);
+		max-width: var(--sk-line-max-width);
+		border-bottom: 1px solid hsl(0, 0%, 87%, 0.2);
+		background: transparent;
+		line-height: 1;
+	}
+
+	.content :global(h3):first-child {
+		border: none;
+		margin: 0;
+	}
+
+	/* avoid doubled border-top */
+	.content :global(h3 > code) {
+		border-radius: 0 0 0 0;
+		border: none;
+		font-size: inherit;
+	}
+
+	.content :global(h4),
+	.content :global(h4 > code) {
+		font-family: inherit;
+		font-weight: 600;
+		font-size: 2.4rem;
+		color: var(--sk-text-2);
+		margin: 6.4rem 0 1.6rem 0;
+		padding-left: 0;
+		background: transparent;
+		line-height: 1;
+		padding-top: 0;
+		top: 0;
+	}
+
+	.content :global(h4::before) {
+		display: inline;
+		content: ' ';
+		block-size: var(--sk-nav-height);
+		margin-block-start: calc(-1 * var(--sk-nav-height));
+	}
+
+	.content :global(h4 > em) {
+		opacity: 0.7;
+	}
+
+	.content :global(h4 > .anchor) {
+		top: 0.05em;
+	}
+
+	.content :global(h5) {
+		font-size: 2.4rem;
+		margin: 2em 0 0.5em 0;
+	}
+
+	.content :global(code) {
+		padding: 0.4rem;
+		margin: 0 0.2rem;
+		top: -0.1rem;
+		background: var(--sk-back-4);
+	}
+
+	.content :global(pre) :global(code) {
+		padding: 0;
+		margin: 0;
+		top: 0;
+		background: transparent;
+	}
+
+	.content :global(pre) {
+		margin: 0 0 2rem 0;
+		width: 100%;
+		max-width: var(--sk-line-max-width);
+		padding: 1rem 1rem;
+		box-shadow: inset 1px 1px 6px hsla(205.7, 63.6%, 30.8%, 0.06);
+	}
+
+	.content :global(.icon) {
+		width: 2rem;
+		height: 2rem;
+		stroke: currentColor;
+		stroke-width: 2;
+		stroke-linecap: round;
+		stroke-linejoin: round;
+		fill: none;
+	}
+
+	.content :global(table) {
+		margin: 0 0 2em 0;
+	}
+
+	.content :global(section) :global(p) {
+		max-width: var(--sk-line-max-width);
+		margin: 1em 0;
+	}
+
+	.content :global(small) {
+		font-size: var(--sk-text-s);
+		float: right;
+		pointer-events: all;
+		color: var(--sk-theme-1);
+		cursor: pointer;
+	}
+
+	.content :global(blockquote) {
+		color: var(--sk-text-1);
+		background-color: rgba(255, 62, 0, 0.1);
+		border-left: 4px solid var(--sk-theme-1-variant);
+		padding: 1rem;
+	}
+
+	.content :global(blockquote) :global(:first-child) {
+		margin-top: 0;
+	}
+
+	.content :global(blockquote) :global(:last-child) {
+		margin-bottom: 0;
+	}
+
+	.content :global(blockquote) :global(code) {
+		background: var(--sk-code-bg);
+	}
+
+	.content :global(section) :global(a):hover {
+		text-decoration: underline;
+	}
+
+	.content :global(section) :global(a) :global(code) {
+		color: inherit;
+		background: rgba(255, 62, 0, 0.1) !important;
+	}
+
+	/* this replaces the offset-anchor hack, which we should remove from this CSS
+	   once https://github.com/sveltejs/action-deploy-docs/issues/1 is closed */
+	.content :global(h2[id]),
+	.content :global(h3[id]) {
+		padding-top: 10rem;
+		margin-top: -2rem;
+		border-top: none;
+	}
+
+	/* .content :global(h2[id])::after {
+		content: '';
+		position: absolute;
+		width: 100%;
+		left: 0;
+		top: 8rem;
+		height: 2px;
+		background: #ddd;
+	} */
+
+	.toc-container {
+		background: var(--sk-back-3);
+	}
+	/* 
+	.ts-toggle {
+		width: 100%;
+		border-top: 1px solid var(--sk-back-4);
+		background-color: var(--sk-back-3);
+	} */
+
+	@media (min-width: 832px) {
+		.toc-container {
+			width: var(--sidebar-width);
+			height: calc(100vh - var(--sk-nav-height) - var(--ts-toggle-height));
+			position: fixed;
+			left: 0;
+			top: var(--sk-nav-height);
+			overflow-x: hidden;
+			overflow-y: auto;
+		}
+
+		.toc-container::before {
+			content: '';
+			position: fixed;
+			width: 0;
+			height: 100%;
+			top: 0;
+			left: calc(var(--sidebar-width) - 1px);
+			border-right: 1px solid var(--sk-back-5);
+		}
+
+		.page {
+			padding-left: calc(var(--sidebar-width) + var(--sk-page-padding-side));
+		}
+
+		/* .ts-toggle {
+			position: fixed;
+			width: var(--sidebar-width);
+			bottom: 0;
+			z-index: 1;
+			margin-right: 0;
+			border-right: 1px solid var(--sk-back-5);
+		} */
+	}
+
+	@media (min-width: 1200px) {
+		.container {
+			--sidebar-width: max(20rem, 18vw);
+		}
+
+		.page {
+			--on-this-page-display: block;
+			padding: var(--sk-page-padding-top) calc(var(--sidebar-width) + var(--sk-page-padding-side));
+			margin: 0 auto;
+			max-width: var(--sk-line-max-width);
+			box-sizing: content-box;
+		}
+	}
+</style>
diff --git a/sites/svelte.dev/src/routes/docs/+page.js b/sites/svelte.dev/src/routes/docs/+page.js
index 5497bd6c2b..dfd22352ff 100644
--- a/sites/svelte.dev/src/routes/docs/+page.js
+++ b/sites/svelte.dev/src/routes/docs/+page.js
@@ -1,12 +1,7 @@
-import { PUBLIC_API_BASE } from '$env/static/public';
+import { base } from '$app/paths';
+import { redirect } from '@sveltejs/kit';
 
 /** @type {import('./$types').PageLoad} */
-export async function load({ fetch, setHeaders }) {
-	const sections = await (await fetch(`${PUBLIC_API_BASE}/docs/svelte/docs?content`)).json();
-
-	setHeaders({
-		'cache-control': 'public, max-age=60'
-	});
-
-	return { sections };
+export async function load() {
+	throw redirect(307, `${base}/docs/introduction`);
 }
diff --git a/sites/svelte.dev/src/routes/docs/+page.svelte b/sites/svelte.dev/src/routes/docs/+page.svelte
deleted file mode 100644
index 878c7915be..0000000000
--- a/sites/svelte.dev/src/routes/docs/+page.svelte
+++ /dev/null
@@ -1,43 +0,0 @@
-<script>
-	// import { Contents, Main, Section } from '@sveltejs/site-kit/docs';
-
-	/** @type {import('./$types').PageData} */
-	export let data;
-
-	let path;
-
-	$: contents = data.sections.map((section) => ({
-		path: `/docs#${section.slug}`,
-		title: section.title,
-		sections: section.sections.map((subsection) => ({
-			path: `/docs#${subsection.slug}`,
-			title: subsection.title,
-			sections: subsection.sections.map((subsection) => ({
-				path: `/docs#${subsection.slug}`,
-				title: subsection.title,
-			})),
-		})),
-	}));
-</script>
-
-<svelte:head>
-	<title>Docs • Svelte</title>
-
-	<meta name="twitter:title" content="Svelte docs" />
-	<meta name="twitter:description" content="Complete documentation for Svelte" />
-	<meta name="Description" content="Complete documentation for Svelte" />
-</svelte:head>
-
-<!-- <Main bind:path>
-	<h1>Documentation</h1>
-
-	{#each data.sections as section}
-		<Section
-			{section}
-			edit="https://github.com/sveltejs/svelte/edit/master/site/content/docs/{section.file}"
-			base="/docs"
-		/>
-	{/each}
-</Main>
-
-<Contents {contents} {path} /> -->
diff --git a/sites/svelte.dev/src/routes/docs/Contents.svelte b/sites/svelte.dev/src/routes/docs/Contents.svelte
new file mode 100644
index 0000000000..373fda4006
--- /dev/null
+++ b/sites/svelte.dev/src/routes/docs/Contents.svelte
@@ -0,0 +1,137 @@
+<script>
+	import { page } from '$app/stores';
+
+	/** @type {{title: string, path: string}[]} */
+	export let contents;
+</script>
+
+<nav aria-label="Docs">
+	<ul class="sidebar">
+		{#each contents as { title, path }}
+			<li>
+				<a
+					data-sveltekit-preload-data
+					class="page"
+					class:active={path === $page.url.pathname}
+					href={path}
+				>
+					{title}
+				</a>
+			</li>
+		{/each}
+	</ul>
+</nav>
+
+<style>
+	nav {
+		top: 0;
+		left: 0;
+		color: var(--sk-text-3);
+
+		display: grid;
+		justify-content: center;
+	}
+
+	.sidebar {
+		padding: var(--sk-page-padding-top) 0 var(--sk-page-padding-top) 3.2rem;
+		font-family: var(--sk-font);
+		height: 100%;
+		bottom: auto;
+		width: 100%;
+		columns: 2;
+		margin: 0;
+	}
+
+	li {
+		display: block;
+		line-height: 1.2;
+		margin: 0;
+		margin-bottom: 1rem;
+	}
+
+	li:last-child {
+		margin-bottom: 0;
+	}
+
+	a {
+		position: relative;
+		transition: color 0.2s;
+		border-bottom: none;
+		padding: 0;
+		color: var(--sk-text-3);
+		user-select: none;
+	}
+
+	.page {
+		display: block;
+		font-size: 1.6rem;
+		font-family: var(--sk-font);
+		padding-bottom: 0.6em;
+	}
+
+	.active {
+		font-weight: 700;
+		color: var(--sk-text-1);
+	}
+
+	@media (min-width: 600px) {
+		.sidebar {
+			columns: 2;
+			padding-left: var(--sk-page-padding-side);
+			padding-right: var(--sk-page-padding-side);
+		}
+	}
+
+	@media (min-width: 700px) {
+		.sidebar {
+			/* columns: 3; */
+		}
+	}
+
+	@media (min-width: 832px) {
+		.sidebar {
+			columns: 1;
+			padding-left: 3.2rem;
+			padding-right: 0;
+			width: var(--sidebar-menu-width);
+			margin: 0 0 0 auto;
+		}
+
+		nav {
+			min-height: calc(100vh - var(--ts-toggle-height));
+		}
+
+		nav::after {
+			content: '';
+			position: fixed;
+			left: 0;
+			bottom: var(--ts-toggle-height);
+			width: calc(var(--sidebar-width) - 1px);
+			height: 2em;
+			pointer-events: none;
+			background: linear-gradient(
+				to bottom,
+				hsla(var(--sk-back-3-hsl), 0) 0%,
+				hsla(var(--sk-back-3-hsl), 0.7) 50%,
+				hsl(var(--sk-back-3-hsl)) 100%
+			);
+			background-repeat: no-repeat;
+			background-size: calc(100% - 3rem) 100%; /* cover text but not scrollbar */
+		}
+
+		.active::after {
+			--size: 1rem;
+			content: '';
+			position: absolute;
+			width: var(--size);
+			height: var(--size);
+			top: -0.1rem;
+			right: calc(-0.5 * var(--size));
+			background-color: var(--sk-back-1);
+			border-left: 1px solid var(--sk-back-5);
+			border-bottom: 1px solid var(--sk-back-5);
+			transform: translateY(0.2rem) rotate(45deg);
+			z-index: 2;
+		}
+	}
+</style>
diff --git a/sites/svelte.dev/src/routes/docs/[slug]/+page.server.js b/sites/svelte.dev/src/routes/docs/[slug]/+page.server.js
new file mode 100644
index 0000000000..a78422af85
--- /dev/null
+++ b/sites/svelte.dev/src/routes/docs/[slug]/+page.server.js
@@ -0,0 +1,24 @@
+import fs from 'fs';
+// import { read_file } from '$lib/server/docs';
+import { error } from '@sveltejs/kit';
+import { read_file } from '$lib/server/docs';
+
+export const prerender = true;
+
+const base = '../../site/content/docs/';
+
+/**
+ * ASSUMPTION FOR FUTURE: This assumes the directory structure of docs is flat. AKA, no nested folders
+ */
+/** @type {import('./$types').PageServerLoad} */
+export async function load({ params }) {
+	for (const file of fs.readdirSync(`${base}`)) {
+		if (file.slice(3, -3) === params.slug) {
+			return {
+				page: await read_file(file),
+			};
+		}
+	}
+
+	throw error(404);
+}
diff --git a/sites/svelte.dev/src/routes/docs/[slug]/+page.svelte b/sites/svelte.dev/src/routes/docs/[slug]/+page.svelte
new file mode 100644
index 0000000000..aa6bfda5b6
--- /dev/null
+++ b/sites/svelte.dev/src/routes/docs/[slug]/+page.svelte
@@ -0,0 +1,16 @@
+<script>
+	import OnThisPage from './OnThisPage.svelte';
+
+	/** @type {import('./$types').PageData}*/
+	export let data;
+</script>
+
+<svelte:head>
+	<title>{data.page.title} - Svelte</title>
+</svelte:head>
+
+{@html data.page.content}
+
+{#if data.page.sections.length !== 0}
+	<OnThisPage details={data.page} />
+{/if}
diff --git a/sites/svelte.dev/src/routes/docs/[slug]/OnThisPage.svelte b/sites/svelte.dev/src/routes/docs/[slug]/OnThisPage.svelte
new file mode 100644
index 0000000000..4085e9e2b4
--- /dev/null
+++ b/sites/svelte.dev/src/routes/docs/[slug]/OnThisPage.svelte
@@ -0,0 +1,141 @@
+<script>
+	import { onMount } from 'svelte';
+	import { afterNavigate } from '$app/navigation';
+	import { base } from '$app/paths';
+	import { page } from '$app/stores';
+
+	/** @type {import('./$types').PageData['page']} */
+	export let details;
+
+	/** @type {string} */
+	let hash = '';
+
+	/** @type {number} */
+	let height = 0;
+
+	/** @type {HTMLElement} */
+	let content;
+
+	/** @type {NodeListOf<HTMLElement>} */
+	let headings;
+
+	/** @type {number[]} */
+	let positions = [];
+
+	onMount(async () => {
+		await document.fonts.ready;
+
+		update();
+		highlight();
+	});
+
+	afterNavigate(() => {
+		update();
+		highlight();
+	});
+
+	function update() {
+		content = document.querySelector('.content');
+		const { top } = content.getBoundingClientRect();
+
+		headings = content.querySelectorAll('h2[id]');
+
+		positions = Array.from(headings).map((heading) => {
+			const style = getComputedStyle(heading);
+			return heading.getBoundingClientRect().top - parseFloat(style.scrollMarginTop) - top;
+		});
+
+		height = window.innerHeight;
+	}
+
+	function highlight() {
+		const { top, bottom } = content.getBoundingClientRect();
+		let i = headings.length;
+
+		while (i--) {
+			if (bottom - height < 50 || positions[i] + top < 100) {
+				const heading = headings[i];
+				hash = `#${heading.id}`;
+				return;
+			}
+		}
+
+		hash = '';
+	}
+
+	/** @param {URL} url */
+	function select(url) {
+		// belt...
+		setTimeout(() => {
+			hash = url.hash;
+		});
+
+		// ...and braces
+		window.addEventListener(
+			'scroll',
+			() => {
+				hash = url.hash;
+			},
+			{ once: true }
+		);
+	}
+</script>
+
+<svelte:window on:scroll={highlight} on:resize={update} on:hashchange={() => select($page.url)} />
+
+<aside class="on-this-page">
+	<h2>On this page</h2>
+	<nav>
+		<ul>
+			<li><a href="{base}/docs/{details.slug}" class:active={hash === ''}>{details.title}</a></li>
+			{#each details.sections as { title, slug }}
+				<li><a href={`#${slug}`} class:active={`#${slug}` === hash}>{title}</a></li>
+			{/each}
+		</ul>
+	</nav>
+</aside>
+
+<style>
+	.on-this-page {
+		display: var(--on-this-page-display);
+		position: fixed;
+		padding: 0 var(--sk-page-padding-side) 0 0;
+		width: min(280px, calc(var(--sidebar-width) - var(--sk-page-padding-side)));
+		/* top: calc(var(--sk-page-padding-top) + var(--sk-nav-height)); */
+		top: var(--sk-nav-height);
+		left: calc(100vw - (var(--sidebar-width)));
+	}
+
+	h2 {
+		text-transform: uppercase;
+		font-size: 1.4rem;
+		font-weight: 400;
+		line-height: 0;
+		margin: 0 0 1rem 0 !important;
+		padding: 0 0 0 0.6rem;
+		color: var(--sk-text-3);
+	}
+
+	ul {
+		list-style: none;
+	}
+
+	a {
+		display: block;
+		padding: 0.3rem 0.5rem;
+		color: var(--sk-text-3);
+		border-left: 2px solid transparent;
+
+		font-size: 1.3rem;
+	}
+
+	a:hover {
+		text-decoration: none;
+		background: var(--sk-back-3);
+	}
+
+	a.active {
+		background: var(--sk-back-3);
+		border-left-color: var(--sk-theme-1);
+	}
+</style>
diff --git a/sites/svelte.dev/svelte.config.js b/sites/svelte.dev/svelte.config.js
index c814fd98a7..68268902e0 100644
--- a/sites/svelte.dev/svelte.config.js
+++ b/sites/svelte.dev/svelte.config.js
@@ -3,6 +3,10 @@ import adapter from '@sveltejs/adapter-auto';
 /** @type {import('@sveltejs/kit').Config} */
 export default {
 	kit: {
-		adapter: adapter()
-	}
+		adapter: adapter(),
+		prerender: {
+			// TODO: REMOVE
+			handleMissingId: 'ignore',
+		},
+	},
 };
diff --git a/sites/svelte.dev/tsconfig.json b/sites/svelte.dev/tsconfig.json
index 73f0a46d97..6484f32c51 100644
--- a/sites/svelte.dev/tsconfig.json
+++ b/sites/svelte.dev/tsconfig.json
@@ -1,3 +1,8 @@
 {
 	"extends": "./.svelte-kit/tsconfig.json",
+	"compilerOptions": {
+		"allowJs": true,
+		"checkJs": true,
+		"allowSyntheticDefaultImports": true
+	}
 }