diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index 15e8b3b051..6369d9d98a 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -35,7 +35,7 @@ jobs:
run: pnpm install --frozen-lockfile
- name: Build Website
env:
- NODE_OPTIONS: '--max_old_space_size=8192'
+ NODE_OPTIONS: '--max_old_space_size=12288'
PUBLIC_APPWRITE_ENDPOINT: ${{ secrets.PUBLIC_APPWRITE_ENDPOINT }}
PUBLIC_APPWRITE_DASHBOARD: ${{ secrets.PUBLIC_APPWRITE_DASHBOARD }}
PUBLIC_APPWRITE_PROJECT_ID: ${{ secrets.PUBLIC_APPWRITE_PROJECT_ID }}
diff --git a/Dockerfile b/Dockerfile
index 55d207bb78..484d4145e3 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -54,7 +54,7 @@ FROM base as build
COPY . .
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile
-RUN NODE_OPTIONS=--max_old_space_size=8192 pnpm run build
+RUN NODE_OPTIONS=--max_old_space_size=12288 pnpm run build
FROM base as final
diff --git a/package.json b/package.json
index dd7b381565..f724fefed1 100644
--- a/package.json
+++ b/package.json
@@ -83,6 +83,8 @@
"vite": "^5.3.1",
"vite-plugin-dynamic-import": "^1.5.0",
"vite-plugin-image-optimizer": "^1.1.8",
- "vitest": "^1.6.0"
+ "vitest": "^1.6.0",
+ "vite-plugin-minify": "^2.0.0",
+ "html-minifier-terser": "^7.2.0"
}
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 642c1e9e34..dd6e9dcd02 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -10,7 +10,7 @@ importers:
dependencies:
'@sentry/sveltekit':
specifier: ^8.12.0
- version: 8.24.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.25.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.25.1)(@sveltejs/kit@2.5.20(@sveltejs/vite-plugin-svelte@3.1.1(svelte@4.2.18)(vite@5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8)))(svelte@4.2.18)(vite@5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8)))(encoding@0.1.13)(svelte@4.2.18)(vite@5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8))
+ version: 8.24.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.25.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.25.1)(@sveltejs/kit@2.5.20(@sveltejs/vite-plugin-svelte@3.1.1(svelte@4.2.18)(vite@5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8)(terser@5.34.1)))(svelte@4.2.18)(vite@5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8)(terser@5.34.1)))(encoding@0.1.13)(svelte@4.2.18)(vite@5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8)(terser@5.34.1))
h3:
specifier: ^1.12.0
version: 1.12.0
@@ -44,16 +44,16 @@ importers:
version: 1.46.0
'@sveltejs/adapter-node':
specifier: ^4.0.1
- version: 4.0.1(@sveltejs/kit@2.5.20(@sveltejs/vite-plugin-svelte@3.1.1(svelte@4.2.18)(vite@5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8)))(svelte@4.2.18)(vite@5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8)))
+ version: 4.0.1(@sveltejs/kit@2.5.20(@sveltejs/vite-plugin-svelte@3.1.1(svelte@4.2.18)(vite@5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8)(terser@5.34.1)))(svelte@4.2.18)(vite@5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8)(terser@5.34.1)))
'@sveltejs/enhanced-img':
specifier: ^0.1.9
version: 0.1.9(rollup@4.20.0)(svelte@4.2.18)
'@sveltejs/kit':
specifier: ^2.5.17
- version: 2.5.20(@sveltejs/vite-plugin-svelte@3.1.1(svelte@4.2.18)(vite@5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8)))(svelte@4.2.18)(vite@5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8))
+ version: 2.5.20(@sveltejs/vite-plugin-svelte@3.1.1(svelte@4.2.18)(vite@5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8)(terser@5.34.1)))(svelte@4.2.18)(vite@5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8)(terser@5.34.1))
'@sveltejs/vite-plugin-svelte':
specifier: ^3.1.1
- version: 3.1.1(svelte@4.2.18)(vite@5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8))
+ version: 3.1.1(svelte@4.2.18)(vite@5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8)(terser@5.34.1))
'@tailwindcss/postcss':
specifier: 4.0.0-alpha.17
version: 4.0.0-alpha.17(postcss@8.4.39)
@@ -111,6 +111,9 @@ importers:
highlight.js:
specifier: ^11.9.0
version: 11.10.0
+ html-minifier-terser:
+ specifier: ^7.2.0
+ version: 7.2.0
markdown-it:
specifier: ^14.1.0
version: 14.1.0
@@ -176,16 +179,19 @@ importers:
version: 5.5.4
vite:
specifier: ^5.3.1
- version: 5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8)
+ version: 5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8)(terser@5.34.1)
vite-plugin-dynamic-import:
specifier: ^1.5.0
version: 1.5.0
vite-plugin-image-optimizer:
specifier: ^1.1.8
- version: 1.1.8(vite@5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8))
+ version: 1.1.8(vite@5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8)(terser@5.34.1))
+ vite-plugin-minify:
+ specifier: ^2.0.0
+ version: 2.0.0(vite@5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8)(terser@5.34.1))
vitest:
specifier: ^1.6.0
- version: 1.6.0(@types/node@22.1.0)(jsdom@20.0.3)(lightningcss@1.25.1)(sass@1.77.8)
+ version: 1.6.0(@types/node@22.1.0)(jsdom@20.0.3)(lightningcss@1.25.1)(sass@1.77.8)(terser@5.34.1)
packages:
@@ -820,6 +826,9 @@ packages:
resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==}
engines: {node: '>=6.0.0'}
+ '@jridgewell/source-map@0.3.6':
+ resolution: {integrity: sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==}
+
'@jridgewell/sourcemap-codec@1.5.0':
resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==}
@@ -1520,6 +1529,9 @@ packages:
'@types/glob@8.1.0':
resolution: {integrity: sha512-IO+MJPVhoqz+28h1qLAcBEH2+xHMK6MTyHJc7MTnnYb6wsoLR29POVGJ7LycmVXIqyy/4/2ShP5sUwTXuOwb/w==}
+ '@types/html-minifier-terser@7.0.2':
+ resolution: {integrity: sha512-mm2HqV22l8lFQh4r2oSsOEVea+m0qqxEmwpc9kC1p/XzmjLWrReR9D/GRs8Pex2NX/imyEH9c5IU/7tMBQCHOA==}
+
'@types/http-errors@2.0.4':
resolution: {integrity: sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==}
@@ -1862,6 +1874,9 @@ packages:
resolution: {integrity: sha512-RgSV6InVQ9ODPdLWJ5UAqBqJBOg370Nz6ZQtRzpt6nUjc8v0St97uJ4PYC6NztqIScrAXafKM3mZPMygSe1ggA==}
engines: {node: '>=0.4.0'}
+ buffer-from@1.1.2:
+ resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
+
buffer@5.7.1:
resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==}
@@ -1885,6 +1900,9 @@ packages:
resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
engines: {node: '>=6'}
+ camel-case@4.1.2:
+ resolution: {integrity: sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==}
+
caniuse-lite@1.0.30001650:
resolution: {integrity: sha512-fgEc7hP/LB7iicdXHUI9VsBsMZmUmlVJeQP2qqQW+3lkqVhbmjEU8zp+h5stWeilX+G7uXuIUIIlWlDw9jdt8g==}
@@ -1931,6 +1949,10 @@ packages:
class-variance-authority@0.7.0:
resolution: {integrity: sha512-jFI8IQw4hczaL4ALINxqLEXQbWcNjoSkloa4IaufXCJr6QawJyw7tuRysRsrE8w2p/4gGaxKIt/hX3qz/IbD1A==}
+ clean-css@5.3.3:
+ resolution: {integrity: sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==}
+ engines: {node: '>= 10.0'}
+
clean-stack@2.2.0:
resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==}
engines: {node: '>=6'}
@@ -1989,6 +2011,13 @@ packages:
resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
engines: {node: '>= 0.8'}
+ commander@10.0.1:
+ resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==}
+ engines: {node: '>=14'}
+
+ commander@2.20.3:
+ resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==}
+
commander@4.1.1:
resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==}
engines: {node: '>= 6'}
@@ -2184,6 +2213,9 @@ packages:
domutils@3.1.0:
resolution: {integrity: sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==}
+ dot-case@3.0.4:
+ resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==}
+
dotenv@16.4.5:
resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==}
engines: {node: '>=12'}
@@ -2553,6 +2585,11 @@ packages:
html-escaper@3.0.3:
resolution: {integrity: sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==}
+ html-minifier-terser@7.2.0:
+ resolution: {integrity: sha512-tXgn3QfqPIpGl9o+K5tpcj3/MN4SfLtsx2GWwBC3SSd0tXQGyF3gsSqad8loJgKZGM3ZxbYDd5yhiBIdWpmvLA==}
+ engines: {node: ^14.13.1 || >=16.0.0}
+ hasBin: true
+
htmlparser2@8.0.2:
resolution: {integrity: sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==}
@@ -2905,6 +2942,9 @@ packages:
lovely-logs@1.2.2:
resolution: {integrity: sha512-dPvYcBok38QvXC+X8/v2BZJDA/iZfCfkRXAwp+s9rvBxY+ZWkMuhNNxukQVIUVMNutIonKpN140Vfv6LCli/wQ==}
+ lower-case@2.0.2:
+ resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==}
+
lru-cache@10.4.3:
resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==}
@@ -3124,6 +3164,9 @@ packages:
resolution: {integrity: sha512-px/KnJAJZf5RuBGcfD+Sp2pAKq0ytz8j+1NehvgIGFkvtvFrDM3T8E4x/JJODXK9WZow8RRGrbA9QQ3hs+pDhA==}
os: ['!win32']
+ no-case@3.0.4:
+ resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==}
+
node-addon-api@3.2.1:
resolution: {integrity: sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==}
@@ -3250,6 +3293,9 @@ packages:
pako@1.0.11:
resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==}
+ param-case@3.0.4:
+ resolution: {integrity: sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==}
+
parent-module@1.0.1:
resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
engines: {node: '>=6'}
@@ -3272,6 +3318,9 @@ packages:
parse5@7.1.2:
resolution: {integrity: sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==}
+ pascal-case@3.1.2:
+ resolution: {integrity: sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==}
+
path-exists@4.0.0:
resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
engines: {node: '>=8'}
@@ -3596,6 +3645,10 @@ packages:
regenerator-runtime@0.14.1:
resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==}
+ relateurl@0.2.7:
+ resolution: {integrity: sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==}
+ engines: {node: '>= 0.10'}
+
remeda@2.10.0:
resolution: {integrity: sha512-2RFz0VWckq8nb0nttzToa8BB5HgLsBHokWUKYiV5C8hnsZOTO1jcx0oXKvfS5khabhS6XkngMOLebQB+pm20IQ==}
@@ -3745,6 +3798,9 @@ packages:
resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==}
engines: {node: '>=0.10.0'}
+ source-map-support@0.5.21:
+ resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==}
+
source-map@0.6.1:
resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
engines: {node: '>=0.10.0'}
@@ -3952,6 +4008,11 @@ packages:
resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==}
engines: {node: '>=10'}
+ terser@5.34.1:
+ resolution: {integrity: sha512-FsJZ7iZLd/BXkz+4xrRTGJ26o/6VTjQytUk8b8OxkwcD2I+79VPJlz7qss1+zE7h8GNIScFqXcDyJ/KqBYZFVA==}
+ engines: {node: '>=10'}
+ hasBin: true
+
text-table@0.2.0:
resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==}
@@ -4146,6 +4207,11 @@ packages:
peerDependencies:
vite: '>=3'
+ vite-plugin-minify@2.0.0:
+ resolution: {integrity: sha512-xQWdXCip/CH3c5a0fftJtvpodOIZqp3gwfuSpGtik/W1YmZKe8WMTJrxvrjgrQ1NcP4EuqmiMCUaz8+If1CPMw==}
+ peerDependencies:
+ vite: ^5.4.0
+
vite@5.3.5:
resolution: {integrity: sha512-MdjglKR6AQXQb9JGiS7Rc2wC6uMjcm7Go/NHNO63EwiJXfuk9PgqiP/n5IDJCziMkfw9n4Ubp7lttNwz+8ZVKA==}
engines: {node: ^18.0.0 || >=20.0.0}
@@ -5055,6 +5121,11 @@ snapshots:
'@jridgewell/set-array@1.2.1': {}
+ '@jridgewell/source-map@0.3.6':
+ dependencies:
+ '@jridgewell/gen-mapping': 0.3.5
+ '@jridgewell/trace-mapping': 0.3.25
+
'@jridgewell/sourcemap-codec@1.5.0': {}
'@jridgewell/trace-mapping@0.3.25':
@@ -5648,7 +5719,7 @@ snapshots:
magic-string: 0.30.7
svelte: 4.2.18
- '@sentry/sveltekit@8.24.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.25.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.25.1)(@sveltejs/kit@2.5.20(@sveltejs/vite-plugin-svelte@3.1.1(svelte@4.2.18)(vite@5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8)))(svelte@4.2.18)(vite@5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8)))(encoding@0.1.13)(svelte@4.2.18)(vite@5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8))':
+ '@sentry/sveltekit@8.24.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.25.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.25.1)(@sveltejs/kit@2.5.20(@sveltejs/vite-plugin-svelte@3.1.1(svelte@4.2.18)(vite@5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8)(terser@5.34.1)))(svelte@4.2.18)(vite@5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8)(terser@5.34.1)))(encoding@0.1.13)(svelte@4.2.18)(vite@5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8)(terser@5.34.1))':
dependencies:
'@sentry/core': 8.24.0
'@sentry/node': 8.24.0
@@ -5657,12 +5728,12 @@ snapshots:
'@sentry/types': 8.24.0
'@sentry/utils': 8.24.0
'@sentry/vite-plugin': 2.20.1(encoding@0.1.13)
- '@sveltejs/kit': 2.5.20(@sveltejs/vite-plugin-svelte@3.1.1(svelte@4.2.18)(vite@5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8)))(svelte@4.2.18)(vite@5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8))
+ '@sveltejs/kit': 2.5.20(@sveltejs/vite-plugin-svelte@3.1.1(svelte@4.2.18)(vite@5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8)(terser@5.34.1)))(svelte@4.2.18)(vite@5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8)(terser@5.34.1))
magic-string: 0.30.7
magicast: 0.2.8
sorcery: 0.11.0
optionalDependencies:
- vite: 5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8)
+ vite: 5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8)(terser@5.34.1)
transitivePeerDependencies:
- '@opentelemetry/api'
- '@opentelemetry/core'
@@ -5697,12 +5768,12 @@ snapshots:
dependencies:
'@sinonjs/commons': 3.0.1
- '@sveltejs/adapter-node@4.0.1(@sveltejs/kit@2.5.20(@sveltejs/vite-plugin-svelte@3.1.1(svelte@4.2.18)(vite@5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8)))(svelte@4.2.18)(vite@5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8)))':
+ '@sveltejs/adapter-node@4.0.1(@sveltejs/kit@2.5.20(@sveltejs/vite-plugin-svelte@3.1.1(svelte@4.2.18)(vite@5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8)(terser@5.34.1)))(svelte@4.2.18)(vite@5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8)(terser@5.34.1)))':
dependencies:
'@rollup/plugin-commonjs': 25.0.8(rollup@4.20.0)
'@rollup/plugin-json': 6.1.0(rollup@4.20.0)
'@rollup/plugin-node-resolve': 15.2.3(rollup@4.20.0)
- '@sveltejs/kit': 2.5.20(@sveltejs/vite-plugin-svelte@3.1.1(svelte@4.2.18)(vite@5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8)))(svelte@4.2.18)(vite@5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8))
+ '@sveltejs/kit': 2.5.20(@sveltejs/vite-plugin-svelte@3.1.1(svelte@4.2.18)(vite@5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8)(terser@5.34.1)))(svelte@4.2.18)(vite@5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8)(terser@5.34.1))
rollup: 4.20.0
'@sveltejs/enhanced-img@0.1.9(rollup@4.20.0)(svelte@4.2.18)':
@@ -5714,9 +5785,9 @@ snapshots:
- rollup
- svelte
- '@sveltejs/kit@2.5.20(@sveltejs/vite-plugin-svelte@3.1.1(svelte@4.2.18)(vite@5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8)))(svelte@4.2.18)(vite@5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8))':
+ '@sveltejs/kit@2.5.20(@sveltejs/vite-plugin-svelte@3.1.1(svelte@4.2.18)(vite@5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8)(terser@5.34.1)))(svelte@4.2.18)(vite@5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8)(terser@5.34.1))':
dependencies:
- '@sveltejs/vite-plugin-svelte': 3.1.1(svelte@4.2.18)(vite@5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8))
+ '@sveltejs/vite-plugin-svelte': 3.1.1(svelte@4.2.18)(vite@5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8)(terser@5.34.1))
'@types/cookie': 0.6.0
cookie: 0.6.0
devalue: 5.0.0
@@ -5730,28 +5801,28 @@ snapshots:
sirv: 2.0.4
svelte: 4.2.18
tiny-glob: 0.2.9
- vite: 5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8)
+ vite: 5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8)(terser@5.34.1)
- '@sveltejs/vite-plugin-svelte-inspector@2.1.0(@sveltejs/vite-plugin-svelte@3.1.1(svelte@4.2.18)(vite@5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8)))(svelte@4.2.18)(vite@5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8))':
+ '@sveltejs/vite-plugin-svelte-inspector@2.1.0(@sveltejs/vite-plugin-svelte@3.1.1(svelte@4.2.18)(vite@5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8)(terser@5.34.1)))(svelte@4.2.18)(vite@5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8)(terser@5.34.1))':
dependencies:
- '@sveltejs/vite-plugin-svelte': 3.1.1(svelte@4.2.18)(vite@5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8))
+ '@sveltejs/vite-plugin-svelte': 3.1.1(svelte@4.2.18)(vite@5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8)(terser@5.34.1))
debug: 4.3.6
svelte: 4.2.18
- vite: 5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8)
+ vite: 5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8)(terser@5.34.1)
transitivePeerDependencies:
- supports-color
- '@sveltejs/vite-plugin-svelte@3.1.1(svelte@4.2.18)(vite@5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8))':
+ '@sveltejs/vite-plugin-svelte@3.1.1(svelte@4.2.18)(vite@5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8)(terser@5.34.1))':
dependencies:
- '@sveltejs/vite-plugin-svelte-inspector': 2.1.0(@sveltejs/vite-plugin-svelte@3.1.1(svelte@4.2.18)(vite@5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8)))(svelte@4.2.18)(vite@5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8))
+ '@sveltejs/vite-plugin-svelte-inspector': 2.1.0(@sveltejs/vite-plugin-svelte@3.1.1(svelte@4.2.18)(vite@5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8)(terser@5.34.1)))(svelte@4.2.18)(vite@5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8)(terser@5.34.1))
debug: 4.3.6
deepmerge: 4.3.1
kleur: 4.1.5
magic-string: 0.30.11
svelte: 4.2.18
svelte-hmr: 0.16.0(svelte@4.2.18)
- vite: 5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8)
- vitefu: 0.2.5(vite@5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8))
+ vite: 5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8)(terser@5.34.1)
+ vitefu: 0.2.5(vite@5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8)(terser@5.34.1))
transitivePeerDependencies:
- supports-color
@@ -5869,6 +5940,8 @@ snapshots:
'@types/minimatch': 5.1.2
'@types/node': 22.1.0
+ '@types/html-minifier-terser@7.0.2': {}
+
'@types/http-errors@2.0.4': {}
'@types/istanbul-lib-coverage@2.0.6': {}
@@ -6237,6 +6310,8 @@ snapshots:
buffer-equal@0.0.1: {}
+ buffer-from@1.1.2: {}
+
buffer@5.7.1:
dependencies:
base64-js: 1.5.1
@@ -6275,6 +6350,11 @@ snapshots:
callsites@3.1.0: {}
+ camel-case@4.1.2:
+ dependencies:
+ pascal-case: 3.1.2
+ tslib: 2.6.3
+
caniuse-lite@1.0.30001650: {}
centra@2.7.0:
@@ -6349,6 +6429,10 @@ snapshots:
dependencies:
clsx: 2.0.0
+ clean-css@5.3.3:
+ dependencies:
+ source-map: 0.6.1
+
clean-stack@2.2.0: {}
cli-progress@3.12.0:
@@ -6409,6 +6493,10 @@ snapshots:
dependencies:
delayed-stream: 1.0.0
+ commander@10.0.1: {}
+
+ commander@2.20.3: {}
+
commander@4.1.1: {}
commander@7.2.0: {}
@@ -6570,6 +6658,11 @@ snapshots:
domelementtype: 2.3.0
domhandler: 5.0.3
+ dot-case@3.0.4:
+ dependencies:
+ no-case: 3.0.4
+ tslib: 2.6.3
+
dotenv@16.4.5: {}
eastasianwidth@0.2.0: {}
@@ -7010,6 +7103,16 @@ snapshots:
html-escaper@3.0.3: {}
+ html-minifier-terser@7.2.0:
+ dependencies:
+ camel-case: 4.1.2
+ clean-css: 5.3.3
+ commander: 10.0.1
+ entities: 4.5.0
+ param-case: 3.0.4
+ relateurl: 0.2.7
+ terser: 5.34.1
+
htmlparser2@8.0.2:
dependencies:
domelementtype: 2.3.0
@@ -7388,6 +7491,10 @@ snapshots:
lovely-logs@1.2.2: {}
+ lower-case@2.0.2:
+ dependencies:
+ tslib: 2.6.3
+
lru-cache@10.4.3: {}
lru-cache@5.1.1:
@@ -7610,6 +7717,11 @@ snapshots:
node-gyp-build: 4.8.1
optional: true
+ no-case@3.0.4:
+ dependencies:
+ lower-case: 2.0.2
+ tslib: 2.6.3
+
node-addon-api@3.2.1:
optional: true
@@ -7762,6 +7874,11 @@ snapshots:
pako@1.0.11: {}
+ param-case@3.0.4:
+ dependencies:
+ dot-case: 3.0.4
+ tslib: 2.6.3
+
parent-module@1.0.1:
dependencies:
callsites: 3.1.0
@@ -7786,6 +7903,11 @@ snapshots:
dependencies:
entities: 4.5.0
+ pascal-case@3.1.2:
+ dependencies:
+ no-case: 3.0.4
+ tslib: 2.6.3
+
path-exists@4.0.0: {}
path-is-absolute@1.0.1: {}
@@ -8021,6 +8143,8 @@ snapshots:
regenerator-runtime@0.14.1: {}
+ relateurl@0.2.7: {}
+
remeda@2.10.0:
dependencies:
type-fest: 4.23.0
@@ -8203,6 +8327,11 @@ snapshots:
source-map-js@1.2.0: {}
+ source-map-support@0.5.21:
+ dependencies:
+ buffer-from: 1.1.2
+ source-map: 0.6.1
+
source-map@0.6.1: {}
split2@1.1.1:
@@ -8445,6 +8574,13 @@ snapshots:
mkdirp: 1.0.4
yallist: 4.0.0
+ terser@5.34.1:
+ dependencies:
+ '@jridgewell/source-map': 0.3.6
+ acorn: 8.12.1
+ commander: 2.20.3
+ source-map-support: 0.5.21
+
text-table@0.2.0: {}
the-new-css-reset@1.11.2: {}
@@ -8615,13 +8751,13 @@ snapshots:
transitivePeerDependencies:
- rollup
- vite-node@1.6.0(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8):
+ vite-node@1.6.0(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8)(terser@5.34.1):
dependencies:
cac: 6.7.14
debug: 4.3.6
pathe: 1.1.2
picocolors: 1.0.1
- vite: 5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8)
+ vite: 5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8)(terser@5.34.1)
transitivePeerDependencies:
- '@types/node'
- less
@@ -8639,13 +8775,19 @@ snapshots:
fast-glob: 3.3.2
magic-string: 0.30.11
- vite-plugin-image-optimizer@1.1.8(vite@5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8)):
+ vite-plugin-image-optimizer@1.1.8(vite@5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8)(terser@5.34.1)):
dependencies:
ansi-colors: 4.1.3
pathe: 1.1.2
- vite: 5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8)
+ vite: 5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8)(terser@5.34.1)
+
+ vite-plugin-minify@2.0.0(vite@5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8)(terser@5.34.1)):
+ dependencies:
+ '@types/html-minifier-terser': 7.0.2
+ html-minifier-terser: 7.2.0
+ vite: 5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8)(terser@5.34.1)
- vite@5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8):
+ vite@5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8)(terser@5.34.1):
dependencies:
esbuild: 0.21.5
postcss: 8.4.41
@@ -8655,12 +8797,13 @@ snapshots:
fsevents: 2.3.3
lightningcss: 1.25.1
sass: 1.77.8
+ terser: 5.34.1
- vitefu@0.2.5(vite@5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8)):
+ vitefu@0.2.5(vite@5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8)(terser@5.34.1)):
optionalDependencies:
- vite: 5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8)
+ vite: 5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8)(terser@5.34.1)
- vitest@1.6.0(@types/node@22.1.0)(jsdom@20.0.3)(lightningcss@1.25.1)(sass@1.77.8):
+ vitest@1.6.0(@types/node@22.1.0)(jsdom@20.0.3)(lightningcss@1.25.1)(sass@1.77.8)(terser@5.34.1):
dependencies:
'@vitest/expect': 1.6.0
'@vitest/runner': 1.6.0
@@ -8679,8 +8822,8 @@ snapshots:
strip-literal: 2.1.0
tinybench: 2.9.0
tinypool: 0.8.4
- vite: 5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8)
- vite-node: 1.6.0(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8)
+ vite: 5.3.5(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8)(terser@5.34.1)
+ vite-node: 1.6.0(@types/node@22.1.0)(lightningcss@1.25.1)(sass@1.77.8)(terser@5.34.1)
why-is-node-running: 2.3.0
optionalDependencies:
'@types/node': 22.1.0
diff --git a/scripts/build.js b/scripts/build.js
index 5141e582a0..1752b494e9 100644
--- a/scripts/build.js
+++ b/scripts/build.js
@@ -1,9 +1,11 @@
import { build } from 'vite';
+import { minifyHtmlPostBuild } from './html-minifier.js';
import { downloadContributors } from './download-contributor-data.js';
async function main() {
await downloadContributors();
await build();
+ await minifyHtmlPostBuild();
}
main();
diff --git a/scripts/html-minifier.js b/scripts/html-minifier.js
new file mode 100644
index 0000000000..ec461efbd8
--- /dev/null
+++ b/scripts/html-minifier.js
@@ -0,0 +1,88 @@
+import path from 'path';
+import fs from 'fs/promises';
+import minifier from 'html-minifier-terser';
+
+const prerenderedPagesDir = 'build/prerendered';
+const htmlMinifierOptions = {
+ minifyJS: true,
+ minifyCSS: true,
+ useShortDoctype: true,
+ collapseWhitespace: true,
+ removeAttributeQuotes: true
+};
+
+async function getHtmlFiles(dir) {
+ let htmlFiles = [];
+ const files = await fs.readdir(dir, { withFileTypes: true });
+
+ for (const file of files) {
+ const filePath = path.join(dir, file.name);
+ if (file.isDirectory()) {
+ htmlFiles = htmlFiles.concat(await getHtmlFiles(filePath));
+ } else if (file.isFile() && file.name.endsWith('.html')) {
+ htmlFiles.push(filePath);
+ }
+ }
+
+ return htmlFiles;
+}
+
+// Function to format sizes in bytes to KB, MB, GB, etc.
+function formatSize(bytes) {
+ if (bytes < 1024) return `${bytes} B`;
+ const units = ['KB', 'MB', 'GB', 'TB'];
+ let value = bytes / 1024;
+ let unitIndex = 0;
+
+ while (value >= 1024 && unitIndex < units.length - 1) {
+ value /= 1024;
+ unitIndex++;
+ }
+
+ return `${value.toFixed(2)} ${units[unitIndex]}`;
+}
+
+export async function minifyHtmlPostBuild() {
+ let minifiedCount = 0;
+ let totalOriginalSize = 0;
+ let totalMinifiedSize = 0;
+
+ const minifyTasks = [];
+
+ console.log('Starting html minification...');
+
+ try {
+ const htmlFiles = await getHtmlFiles(prerenderedPagesDir);
+
+ for (const htmlPath of htmlFiles) {
+ minifyTasks.push(
+ (async () => {
+ try {
+ const html = await fs.readFile(htmlPath, 'utf-8');
+ const originalSize = Buffer.byteLength(html, 'utf-8');
+ totalOriginalSize += originalSize;
+
+ const minHTML = await minifier.minify(html, htmlMinifierOptions);
+ const minifiedSize = Buffer.byteLength(minHTML, 'utf-8');
+ totalMinifiedSize += minifiedSize;
+
+ await fs.writeFile(htmlPath, minHTML);
+
+ minifiedCount += 1;
+ } catch (error) {
+ console.error(`Failed to minify HTML for ${htmlPath}: ${error.message}`);
+ }
+ })()
+ );
+ }
+
+ await Promise.all(minifyTasks);
+
+ console.log(`Minification complete: ${minifiedCount} files processed.`);
+ console.log(
+ `Original: ${formatSize(totalOriginalSize)}, Minified: ${formatSize(totalMinifiedSize)}`
+ );
+ } catch (error) {
+ console.error(`Error processing files: ${error.message}`);
+ }
+}
diff --git a/scripts/thumbnails.js b/scripts/thumbnails.js
new file mode 100644
index 0000000000..773ae8ffef
--- /dev/null
+++ b/scripts/thumbnails.js
@@ -0,0 +1,179 @@
+import { mkdirSync, readdirSync, readFileSync } from 'fs';
+import { dirname, join } from 'path';
+import sharp from 'sharp';
+import { fileURLToPath } from 'url';
+
+const __dirname = dirname(fileURLToPath(import.meta.url));
+// Define the base path for static images
+const srcDir = join(__dirname, '../static/images');
+
+const authorDir = join(srcDir, 'avatars');
+const blogCoverDir = join(srcDir, 'blog');
+const authorDestDir = join(authorDir, 'thumbnails');
+const blogCoverDestDir = join(blogCoverDir, 'thumbnails');
+
+const articlesDir = join(__dirname, '../src/routes/blog/post');
+const authorsDir = join(__dirname, '../src/routes/blog/author');
+
+function isPNG(file) {
+ return file.endsWith('.png');
+}
+
+function parseFrontmatter(file, key = 'cover') {
+ const content = readFileSync(file, 'utf8');
+ const fmRegex = /---\s*([\s\S]*?)\s*---/;
+ const searchForFeaturedPost = key === 'cover';
+
+ const featuredRegex = new RegExp(`^featured:\\s*['"]?(.+?)['"]?$`, 'm');
+
+ const match = content.match(fmRegex);
+ if (match) {
+ const fmContent = match[1];
+ const regex = new RegExp(`^${key}:\\s*(.+)$`, 'm');
+ const imageMatch = fmContent.match(regex);
+ if (imageMatch) {
+ let withFeature = {};
+ withFeature.image = imageMatch[1].trim();
+
+ if (searchForFeaturedPost) {
+ const featureMatch = fmContent.match(featuredRegex);
+ if (featureMatch) {
+ withFeature.featured = featureMatch[1] === 'true';
+ } else {
+ withFeature.featured = false;
+ }
+ }
+
+ return withFeature;
+ }
+ }
+ return null;
+}
+
+function walkDirectory(dir, list = []) {
+ const files = readdirSync(dir, { withFileTypes: true });
+ files.forEach((file) => {
+ const pathToFile = join(dir, file.name);
+ if (file.isDirectory()) {
+ list = walkDirectory(pathToFile, list);
+ } else if (file.name === '+page.markdoc') {
+ list.push(pathToFile);
+ }
+ });
+
+ return list;
+}
+
+function ensureDir(path) {
+ try {
+ mkdirSync(path, { recursive: true });
+ } catch (err) {
+ if (err.code !== 'EEXIST') throw err;
+ }
+}
+
+async function createThumbnails(images, destDir, options) {
+ const { width, height } = options;
+
+ for (const file of images) {
+ let relativePath = file.substring(srcDir.length).replace(/\/blog\/|\/avatars\//, '');
+ const thumbBasePath = join(destDir, relativePath);
+
+ ensureDir(dirname(thumbBasePath));
+
+ const pngThumbPath = thumbBasePath.replace(/\.[^/.]+$/, '.png');
+ const webpThumbPath = thumbBasePath.replace(/\.[^/.]+$/, '.webp');
+
+ const resizedImageBuffer = await sharp(file)
+ .resize({
+ width,
+ height,
+ fit: sharp.fit.inside,
+ withoutEnlargement: true
+ })
+ .toBuffer();
+
+ const sharpInstance = sharp(resizedImageBuffer);
+
+ await Promise.all([
+ sharpInstance.webp({ lossless: true }).toFile(webpThumbPath),
+ sharpInstance.png({ compressionLevel: 9 }).toFile(pngThumbPath)
+ ]);
+ }
+}
+
+function getBlogCovers() {
+ const markdocFiles = walkDirectory(articlesDir);
+ return markdocFiles
+ .map((filePath) => {
+ const { featured, image: coverPath } = parseFrontmatter(filePath);
+
+ if (coverPath && isPNG(coverPath)) {
+ return {
+ featured: featured,
+ coverPath: join(__dirname, '../static', coverPath)
+ };
+ }
+ })
+ .filter(Boolean);
+}
+
+function getAuthorAvatars() {
+ const authorFiles = walkDirectory(authorsDir);
+ return authorFiles
+ .map((filePath) => {
+ const { image: avatarPath } = parseFrontmatter(filePath, 'avatar');
+ if (avatarPath && isPNG(avatarPath)) {
+ return join(__dirname, '../static', avatarPath);
+ }
+ })
+ .filter(Boolean);
+}
+
+/**
+ * @typedef {{ width: number, height: number }} ThumbnailSize
+ *
+ * @typedef {{
+ * cover: {
+ * normal: ThumbnailSize,
+ * featured: ThumbnailSize
+ * },
+ * author: ThumbnailSize
+ * }} ThumbnailOptions
+ */
+
+/**
+ * Preprocess thumbnails for cover and author images.
+ *
+ * @param {Object} [options={}] - Options to configure the thumbnail sizes.
+ * @param {{
+ * cover?: {
+ * normal?: ThumbnailSize,
+ * featured?: ThumbnailSize
+ * },
+ * author?: ThumbnailSize
+ * }} [options] - The optional thumbnail configuration.
+ */
+export async function thumbnailPreprocess(options = {}) {
+ const {
+ cover: {
+ normal: normalCover = { width: 320, height: 320 },
+ featured: featuredCover = { width: 640, height: 640 }
+ } = {},
+ author: authorOption = { width: 112, height: 112 }
+ } = options;
+
+ const coverImages = getBlogCovers();
+ const authorAvatars = getAuthorAvatars();
+
+ const normalCovers = coverImages.filter((img) => !img.featured).map((img) => img.coverPath);
+ const featuredCovers = coverImages.filter((img) => img.featured).map((img) => img.coverPath);
+
+ await Promise.all([
+ createThumbnails(authorAvatars, authorDestDir, authorOption),
+ createThumbnails(normalCovers, blogCoverDestDir, normalCover),
+ createThumbnails(featuredCovers, blogCoverDestDir, featuredCover)
+ ]);
+
+ return { name: 'thumbnail-creator-preprocessor' };
+}
diff --git a/src/icons/output/info.json b/src/icons/output/info.json
index 6e9be45ece..fc00f8f508 100644
--- a/src/icons/output/info.json
+++ b/src/icons/output/info.json
@@ -1,314 +1,314 @@
{
- "apple": {
- "encodedCode": "\\ea01",
- "prefix": "web-icon",
- "className": "web-icon-apple",
- "unicode": ""
- },
- "appwrite": {
- "encodedCode": "\\ea02",
- "prefix": "web-icon",
- "className": "web-icon-appwrite",
- "unicode": ""
- },
- "arrow-down": {
- "encodedCode": "\\ea03",
- "prefix": "web-icon",
- "className": "web-icon-arrow-down",
- "unicode": ""
- },
- "arrow-ext-link": {
- "encodedCode": "\\ea04",
- "prefix": "web-icon",
- "className": "web-icon-arrow-ext-link",
- "unicode": ""
- },
- "arrow-left": {
- "encodedCode": "\\ea05",
- "prefix": "web-icon",
- "className": "web-icon-arrow-left",
- "unicode": ""
- },
- "arrow-right": {
- "encodedCode": "\\ea06",
- "prefix": "web-icon",
- "className": "web-icon-arrow-right",
- "unicode": ""
- },
- "arrow-up": {
- "encodedCode": "\\ea07",
- "prefix": "web-icon",
- "className": "web-icon-arrow-up",
- "unicode": ""
- },
- "calendar": {
- "encodedCode": "\\ea08",
- "prefix": "web-icon",
- "className": "web-icon-calendar",
- "unicode": ""
- },
- "check": {
- "encodedCode": "\\ea09",
- "prefix": "web-icon",
- "className": "web-icon-check",
- "unicode": ""
- },
- "chevron-down": {
- "encodedCode": "\\ea0a",
- "prefix": "web-icon",
- "className": "web-icon-chevron-down",
- "unicode": ""
- },
- "chevron-left": {
- "encodedCode": "\\ea0b",
- "prefix": "web-icon",
- "className": "web-icon-chevron-left",
- "unicode": ""
- },
- "chevron-right": {
- "encodedCode": "\\ea0c",
- "prefix": "web-icon",
- "className": "web-icon-chevron-right",
- "unicode": ""
- },
- "chevron-up": {
- "encodedCode": "\\ea0d",
- "prefix": "web-icon",
- "className": "web-icon-chevron-up",
- "unicode": ""
- },
- "close": {
- "encodedCode": "\\ea0e",
- "prefix": "web-icon",
- "className": "web-icon-close",
- "unicode": ""
- },
- "command": {
- "encodedCode": "\\ea0f",
- "prefix": "web-icon",
- "className": "web-icon-command",
- "unicode": ""
- },
- "copy": {
- "encodedCode": "\\ea10",
- "prefix": "web-icon",
- "className": "web-icon-copy",
- "unicode": ""
- },
- "daily-dev": {
- "encodedCode": "\\ea11",
- "prefix": "web-icon",
- "className": "web-icon-daily-dev",
- "unicode": ""
- },
- "dark": {
- "encodedCode": "\\ea12",
- "prefix": "web-icon",
- "className": "web-icon-dark",
- "unicode": ""
- },
- "discord": {
- "encodedCode": "\\ea13",
- "prefix": "web-icon",
- "className": "web-icon-discord",
- "unicode": ""
- },
- "divider-vertical": {
- "encodedCode": "\\ea14",
- "prefix": "web-icon",
- "className": "web-icon-divider-vertical",
- "unicode": ""
- },
- "download": {
- "encodedCode": "\\ea15",
- "prefix": "web-icon",
- "className": "web-icon-download",
- "unicode": ""
- },
- "ext-link": {
- "encodedCode": "\\ea16",
- "prefix": "web-icon",
- "className": "web-icon-ext-link",
- "unicode": ""
- },
- "firebase": {
- "encodedCode": "\\ea17",
- "prefix": "web-icon",
- "className": "web-icon-firebase",
- "unicode": ""
- },
- "github": {
- "encodedCode": "\\ea18",
- "prefix": "web-icon",
- "className": "web-icon-github",
- "unicode": ""
- },
- "google": {
- "encodedCode": "\\ea19",
- "prefix": "web-icon",
- "className": "web-icon-google",
- "unicode": ""
- },
- "hamburger-menu": {
- "encodedCode": "\\ea1a",
- "prefix": "web-icon",
- "className": "web-icon-hamburger-menu",
- "unicode": ""
- },
- "light": {
- "encodedCode": "\\ea1b",
- "prefix": "web-icon",
- "className": "web-icon-light",
- "unicode": ""
- },
- "linkedin": {
- "encodedCode": "\\ea1c",
- "prefix": "web-icon",
- "className": "web-icon-linkedin",
- "unicode": ""
- },
- "location": {
- "encodedCode": "\\ea1d",
- "prefix": "web-icon",
- "className": "web-icon-location",
- "unicode": ""
- },
- "logout-left": {
- "encodedCode": "\\ea1e",
- "prefix": "web-icon",
- "className": "web-icon-logout-left",
- "unicode": ""
- },
- "logout-right": {
- "encodedCode": "\\ea1f",
- "prefix": "web-icon",
- "className": "web-icon-logout-right",
- "unicode": ""
- },
- "mailgun": {
- "encodedCode": "\\ea20",
- "prefix": "web-icon",
- "className": "web-icon-mailgun",
- "unicode": ""
- },
- "message": {
- "encodedCode": "\\ea21",
- "prefix": "web-icon",
- "className": "web-icon-message",
- "unicode": ""
- },
- "microsoft": {
- "encodedCode": "\\ea22",
- "prefix": "web-icon",
- "className": "web-icon-microsoft",
- "unicode": ""
- },
- "minus": {
- "encodedCode": "\\ea23",
- "prefix": "web-icon",
- "className": "web-icon-minus",
- "unicode": ""
- },
- "nuxt": {
- "encodedCode": "\\ea24",
- "prefix": "web-icon",
- "className": "web-icon-nuxt",
- "unicode": ""
- },
- "platform": {
- "encodedCode": "\\ea25",
- "prefix": "web-icon",
- "className": "web-icon-platform",
- "unicode": ""
- },
- "play": {
- "encodedCode": "\\ea26",
- "prefix": "web-icon",
- "className": "web-icon-play",
- "unicode": ""
- },
- "plus": {
- "encodedCode": "\\ea27",
- "prefix": "web-icon",
- "className": "web-icon-plus",
- "unicode": ""
- },
- "product-hunt": {
- "encodedCode": "\\ea28",
- "prefix": "web-icon",
- "className": "web-icon-product-hunt",
- "unicode": ""
- },
- "refine": {
- "encodedCode": "\\ea29",
- "prefix": "web-icon",
- "className": "web-icon-refine",
- "unicode": ""
- },
- "rest": {
- "encodedCode": "\\ea2a",
- "prefix": "web-icon",
- "className": "web-icon-rest",
- "unicode": ""
- },
- "search": {
- "encodedCode": "\\ea2b",
- "prefix": "web-icon",
- "className": "web-icon-search",
- "unicode": ""
- },
- "sendgrid": {
- "encodedCode": "\\ea2c",
- "prefix": "web-icon",
- "className": "web-icon-sendgrid",
- "unicode": ""
- },
- "star": {
- "encodedCode": "\\ea2d",
- "prefix": "web-icon",
- "className": "web-icon-star",
- "unicode": ""
- },
- "system": {
- "encodedCode": "\\ea2e",
- "prefix": "web-icon",
- "className": "web-icon-system",
- "unicode": ""
- },
- "textmagic": {
- "encodedCode": "\\ea2f",
- "prefix": "web-icon",
- "className": "web-icon-textmagic",
- "unicode": ""
- },
- "twitter": {
- "encodedCode": "\\ea30",
- "prefix": "web-icon",
- "className": "web-icon-twitter",
- "unicode": ""
- },
- "vue": {
- "encodedCode": "\\ea31",
- "prefix": "web-icon",
- "className": "web-icon-vue",
- "unicode": ""
- },
- "x": {
- "encodedCode": "\\ea32",
- "prefix": "web-icon",
- "className": "web-icon-x",
- "unicode": ""
- },
- "ycombinator": {
- "encodedCode": "\\ea33",
- "prefix": "web-icon",
- "className": "web-icon-ycombinator",
- "unicode": ""
- },
- "youtube": {
- "encodedCode": "\\ea34",
- "prefix": "web-icon",
- "className": "web-icon-youtube",
- "unicode": ""
- }
+ "apple": {
+ "encodedCode": "\\ea01",
+ "prefix": "web-icon",
+ "className": "web-icon-apple",
+ "unicode": ""
+ },
+ "appwrite": {
+ "encodedCode": "\\ea02",
+ "prefix": "web-icon",
+ "className": "web-icon-appwrite",
+ "unicode": ""
+ },
+ "arrow-down": {
+ "encodedCode": "\\ea03",
+ "prefix": "web-icon",
+ "className": "web-icon-arrow-down",
+ "unicode": ""
+ },
+ "arrow-ext-link": {
+ "encodedCode": "\\ea04",
+ "prefix": "web-icon",
+ "className": "web-icon-arrow-ext-link",
+ "unicode": ""
+ },
+ "arrow-left": {
+ "encodedCode": "\\ea05",
+ "prefix": "web-icon",
+ "className": "web-icon-arrow-left",
+ "unicode": ""
+ },
+ "arrow-right": {
+ "encodedCode": "\\ea06",
+ "prefix": "web-icon",
+ "className": "web-icon-arrow-right",
+ "unicode": ""
+ },
+ "arrow-up": {
+ "encodedCode": "\\ea07",
+ "prefix": "web-icon",
+ "className": "web-icon-arrow-up",
+ "unicode": ""
+ },
+ "calendar": {
+ "encodedCode": "\\ea08",
+ "prefix": "web-icon",
+ "className": "web-icon-calendar",
+ "unicode": ""
+ },
+ "check": {
+ "encodedCode": "\\ea09",
+ "prefix": "web-icon",
+ "className": "web-icon-check",
+ "unicode": ""
+ },
+ "chevron-down": {
+ "encodedCode": "\\ea0a",
+ "prefix": "web-icon",
+ "className": "web-icon-chevron-down",
+ "unicode": ""
+ },
+ "chevron-left": {
+ "encodedCode": "\\ea0b",
+ "prefix": "web-icon",
+ "className": "web-icon-chevron-left",
+ "unicode": ""
+ },
+ "chevron-right": {
+ "encodedCode": "\\ea0c",
+ "prefix": "web-icon",
+ "className": "web-icon-chevron-right",
+ "unicode": ""
+ },
+ "chevron-up": {
+ "encodedCode": "\\ea0d",
+ "prefix": "web-icon",
+ "className": "web-icon-chevron-up",
+ "unicode": ""
+ },
+ "close": {
+ "encodedCode": "\\ea0e",
+ "prefix": "web-icon",
+ "className": "web-icon-close",
+ "unicode": ""
+ },
+ "command": {
+ "encodedCode": "\\ea0f",
+ "prefix": "web-icon",
+ "className": "web-icon-command",
+ "unicode": ""
+ },
+ "copy": {
+ "encodedCode": "\\ea10",
+ "prefix": "web-icon",
+ "className": "web-icon-copy",
+ "unicode": ""
+ },
+ "daily-dev": {
+ "encodedCode": "\\ea11",
+ "prefix": "web-icon",
+ "className": "web-icon-daily-dev",
+ "unicode": ""
+ },
+ "dark": {
+ "encodedCode": "\\ea12",
+ "prefix": "web-icon",
+ "className": "web-icon-dark",
+ "unicode": ""
+ },
+ "discord": {
+ "encodedCode": "\\ea13",
+ "prefix": "web-icon",
+ "className": "web-icon-discord",
+ "unicode": ""
+ },
+ "divider-vertical": {
+ "encodedCode": "\\ea14",
+ "prefix": "web-icon",
+ "className": "web-icon-divider-vertical",
+ "unicode": ""
+ },
+ "download": {
+ "encodedCode": "\\ea15",
+ "prefix": "web-icon",
+ "className": "web-icon-download",
+ "unicode": ""
+ },
+ "ext-link": {
+ "encodedCode": "\\ea16",
+ "prefix": "web-icon",
+ "className": "web-icon-ext-link",
+ "unicode": ""
+ },
+ "firebase": {
+ "encodedCode": "\\ea17",
+ "prefix": "web-icon",
+ "className": "web-icon-firebase",
+ "unicode": ""
+ },
+ "github": {
+ "encodedCode": "\\ea18",
+ "prefix": "web-icon",
+ "className": "web-icon-github",
+ "unicode": ""
+ },
+ "google": {
+ "encodedCode": "\\ea19",
+ "prefix": "web-icon",
+ "className": "web-icon-google",
+ "unicode": ""
+ },
+ "hamburger-menu": {
+ "encodedCode": "\\ea1a",
+ "prefix": "web-icon",
+ "className": "web-icon-hamburger-menu",
+ "unicode": ""
+ },
+ "light": {
+ "encodedCode": "\\ea1b",
+ "prefix": "web-icon",
+ "className": "web-icon-light",
+ "unicode": ""
+ },
+ "linkedin": {
+ "encodedCode": "\\ea1c",
+ "prefix": "web-icon",
+ "className": "web-icon-linkedin",
+ "unicode": ""
+ },
+ "location": {
+ "encodedCode": "\\ea1d",
+ "prefix": "web-icon",
+ "className": "web-icon-location",
+ "unicode": ""
+ },
+ "logout-left": {
+ "encodedCode": "\\ea1e",
+ "prefix": "web-icon",
+ "className": "web-icon-logout-left",
+ "unicode": ""
+ },
+ "logout-right": {
+ "encodedCode": "\\ea1f",
+ "prefix": "web-icon",
+ "className": "web-icon-logout-right",
+ "unicode": ""
+ },
+ "mailgun": {
+ "encodedCode": "\\ea20",
+ "prefix": "web-icon",
+ "className": "web-icon-mailgun",
+ "unicode": ""
+ },
+ "message": {
+ "encodedCode": "\\ea21",
+ "prefix": "web-icon",
+ "className": "web-icon-message",
+ "unicode": ""
+ },
+ "microsoft": {
+ "encodedCode": "\\ea22",
+ "prefix": "web-icon",
+ "className": "web-icon-microsoft",
+ "unicode": ""
+ },
+ "minus": {
+ "encodedCode": "\\ea23",
+ "prefix": "web-icon",
+ "className": "web-icon-minus",
+ "unicode": ""
+ },
+ "nuxt": {
+ "encodedCode": "\\ea24",
+ "prefix": "web-icon",
+ "className": "web-icon-nuxt",
+ "unicode": ""
+ },
+ "platform": {
+ "encodedCode": "\\ea25",
+ "prefix": "web-icon",
+ "className": "web-icon-platform",
+ "unicode": ""
+ },
+ "play": {
+ "encodedCode": "\\ea26",
+ "prefix": "web-icon",
+ "className": "web-icon-play",
+ "unicode": ""
+ },
+ "plus": {
+ "encodedCode": "\\ea27",
+ "prefix": "web-icon",
+ "className": "web-icon-plus",
+ "unicode": ""
+ },
+ "product-hunt": {
+ "encodedCode": "\\ea28",
+ "prefix": "web-icon",
+ "className": "web-icon-product-hunt",
+ "unicode": ""
+ },
+ "refine": {
+ "encodedCode": "\\ea29",
+ "prefix": "web-icon",
+ "className": "web-icon-refine",
+ "unicode": ""
+ },
+ "rest": {
+ "encodedCode": "\\ea2a",
+ "prefix": "web-icon",
+ "className": "web-icon-rest",
+ "unicode": ""
+ },
+ "search": {
+ "encodedCode": "\\ea2b",
+ "prefix": "web-icon",
+ "className": "web-icon-search",
+ "unicode": ""
+ },
+ "sendgrid": {
+ "encodedCode": "\\ea2c",
+ "prefix": "web-icon",
+ "className": "web-icon-sendgrid",
+ "unicode": ""
+ },
+ "star": {
+ "encodedCode": "\\ea2d",
+ "prefix": "web-icon",
+ "className": "web-icon-star",
+ "unicode": ""
+ },
+ "system": {
+ "encodedCode": "\\ea2e",
+ "prefix": "web-icon",
+ "className": "web-icon-system",
+ "unicode": ""
+ },
+ "textmagic": {
+ "encodedCode": "\\ea2f",
+ "prefix": "web-icon",
+ "className": "web-icon-textmagic",
+ "unicode": ""
+ },
+ "twitter": {
+ "encodedCode": "\\ea30",
+ "prefix": "web-icon",
+ "className": "web-icon-twitter",
+ "unicode": ""
+ },
+ "vue": {
+ "encodedCode": "\\ea31",
+ "prefix": "web-icon",
+ "className": "web-icon-vue",
+ "unicode": ""
+ },
+ "x": {
+ "encodedCode": "\\ea32",
+ "prefix": "web-icon",
+ "className": "web-icon-x",
+ "unicode": ""
+ },
+ "ycombinator": {
+ "encodedCode": "\\ea33",
+ "prefix": "web-icon",
+ "className": "web-icon-ycombinator",
+ "unicode": ""
+ },
+ "youtube": {
+ "encodedCode": "\\ea34",
+ "prefix": "web-icon",
+ "className": "web-icon-youtube",
+ "unicode": ""
+ }
}
diff --git a/src/icons/output/web-icon.css b/src/icons/output/web-icon.css
index 22208dc214..1946420c21 100644
--- a/src/icons/output/web-icon.css
+++ b/src/icons/output/web-icon.css
@@ -1,15 +1,18 @@
@font-face {
- font-family: "web-icon";
+ font-family: 'web-icon';
font-display: swap;
src: url('web-icon.eot'); /* IE9*/
- src: url('web-icon.eot#iefix') format('embedded-opentype'), /* IE6-IE8 */
- url("web-icon.woff2") format("woff2"),
- url("web-icon.woff") format("woff"),
- url('web-icon.ttf') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/
- url('web-icon.svg#web-icon') format('svg'); /* iOS 4.1- */
+ src:
+ url('web-icon.eot#iefix') format('embedded-opentype'),
+ /* IE6-IE8 */ url('web-icon.woff2') format('woff2'),
+ url('web-icon.woff') format('woff'),
+ url('web-icon.ttf') format('truetype'),
+ /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/ url('web-icon.svg#web-icon')
+ format('svg'); /* iOS 4.1- */
}
-[class^="web-icon-"], [class*=" web-icon-"] {
+[class^='web-icon-'],
+[class*=' web-icon-'] {
font-family: 'web-icon' !important;
font-size: 20px;
font-style: normal;
@@ -17,55 +20,159 @@
-moz-osx-font-smoothing: grayscale;
}
-.web-icon-apple:before { content: "\ea01"; }
-.web-icon-appwrite:before { content: "\ea02"; }
-.web-icon-arrow-down:before { content: "\ea03"; }
-.web-icon-arrow-ext-link:before { content: "\ea04"; }
-.web-icon-arrow-left:before { content: "\ea05"; }
-.web-icon-arrow-right:before { content: "\ea06"; }
-.web-icon-arrow-up:before { content: "\ea07"; }
-.web-icon-calendar:before { content: "\ea08"; }
-.web-icon-check:before { content: "\ea09"; }
-.web-icon-chevron-down:before { content: "\ea0a"; }
-.web-icon-chevron-left:before { content: "\ea0b"; }
-.web-icon-chevron-right:before { content: "\ea0c"; }
-.web-icon-chevron-up:before { content: "\ea0d"; }
-.web-icon-close:before { content: "\ea0e"; }
-.web-icon-command:before { content: "\ea0f"; }
-.web-icon-copy:before { content: "\ea10"; }
-.web-icon-daily-dev:before { content: "\ea11"; }
-.web-icon-dark:before { content: "\ea12"; }
-.web-icon-discord:before { content: "\ea13"; }
-.web-icon-divider-vertical:before { content: "\ea14"; }
-.web-icon-download:before { content: "\ea15"; }
-.web-icon-ext-link:before { content: "\ea16"; }
-.web-icon-firebase:before { content: "\ea17"; }
-.web-icon-github:before { content: "\ea18"; }
-.web-icon-google:before { content: "\ea19"; }
-.web-icon-hamburger-menu:before { content: "\ea1a"; }
-.web-icon-light:before { content: "\ea1b"; }
-.web-icon-linkedin:before { content: "\ea1c"; }
-.web-icon-location:before { content: "\ea1d"; }
-.web-icon-logout-left:before { content: "\ea1e"; }
-.web-icon-logout-right:before { content: "\ea1f"; }
-.web-icon-mailgun:before { content: "\ea20"; }
-.web-icon-message:before { content: "\ea21"; }
-.web-icon-microsoft:before { content: "\ea22"; }
-.web-icon-minus:before { content: "\ea23"; }
-.web-icon-nuxt:before { content: "\ea24"; }
-.web-icon-platform:before { content: "\ea25"; }
-.web-icon-play:before { content: "\ea26"; }
-.web-icon-plus:before { content: "\ea27"; }
-.web-icon-product-hunt:before { content: "\ea28"; }
-.web-icon-refine:before { content: "\ea29"; }
-.web-icon-rest:before { content: "\ea2a"; }
-.web-icon-search:before { content: "\ea2b"; }
-.web-icon-sendgrid:before { content: "\ea2c"; }
-.web-icon-star:before { content: "\ea2d"; }
-.web-icon-system:before { content: "\ea2e"; }
-.web-icon-textmagic:before { content: "\ea2f"; }
-.web-icon-twitter:before { content: "\ea30"; }
-.web-icon-vue:before { content: "\ea31"; }
-.web-icon-x:before { content: "\ea32"; }
-.web-icon-ycombinator:before { content: "\ea33"; }
-.web-icon-youtube:before { content: "\ea34"; }
+.web-icon-apple:before {
+ content: '\ea01';
+}
+.web-icon-appwrite:before {
+ content: '\ea02';
+}
+.web-icon-arrow-down:before {
+ content: '\ea03';
+}
+.web-icon-arrow-ext-link:before {
+ content: '\ea04';
+}
+.web-icon-arrow-left:before {
+ content: '\ea05';
+}
+.web-icon-arrow-right:before {
+ content: '\ea06';
+}
+.web-icon-arrow-up:before {
+ content: '\ea07';
+}
+.web-icon-calendar:before {
+ content: '\ea08';
+}
+.web-icon-check:before {
+ content: '\ea09';
+}
+.web-icon-chevron-down:before {
+ content: '\ea0a';
+}
+.web-icon-chevron-left:before {
+ content: '\ea0b';
+}
+.web-icon-chevron-right:before {
+ content: '\ea0c';
+}
+.web-icon-chevron-up:before {
+ content: '\ea0d';
+}
+.web-icon-close:before {
+ content: '\ea0e';
+}
+.web-icon-command:before {
+ content: '\ea0f';
+}
+.web-icon-copy:before {
+ content: '\ea10';
+}
+.web-icon-daily-dev:before {
+ content: '\ea11';
+}
+.web-icon-dark:before {
+ content: '\ea12';
+}
+.web-icon-discord:before {
+ content: '\ea13';
+}
+.web-icon-divider-vertical:before {
+ content: '\ea14';
+}
+.web-icon-download:before {
+ content: '\ea15';
+}
+.web-icon-ext-link:before {
+ content: '\ea16';
+}
+.web-icon-firebase:before {
+ content: '\ea17';
+}
+.web-icon-github:before {
+ content: '\ea18';
+}
+.web-icon-google:before {
+ content: '\ea19';
+}
+.web-icon-hamburger-menu:before {
+ content: '\ea1a';
+}
+.web-icon-light:before {
+ content: '\ea1b';
+}
+.web-icon-linkedin:before {
+ content: '\ea1c';
+}
+.web-icon-location:before {
+ content: '\ea1d';
+}
+.web-icon-logout-left:before {
+ content: '\ea1e';
+}
+.web-icon-logout-right:before {
+ content: '\ea1f';
+}
+.web-icon-mailgun:before {
+ content: '\ea20';
+}
+.web-icon-message:before {
+ content: '\ea21';
+}
+.web-icon-microsoft:before {
+ content: '\ea22';
+}
+.web-icon-minus:before {
+ content: '\ea23';
+}
+.web-icon-nuxt:before {
+ content: '\ea24';
+}
+.web-icon-platform:before {
+ content: '\ea25';
+}
+.web-icon-play:before {
+ content: '\ea26';
+}
+.web-icon-plus:before {
+ content: '\ea27';
+}
+.web-icon-product-hunt:before {
+ content: '\ea28';
+}
+.web-icon-refine:before {
+ content: '\ea29';
+}
+.web-icon-rest:before {
+ content: '\ea2a';
+}
+.web-icon-search:before {
+ content: '\ea2b';
+}
+.web-icon-sendgrid:before {
+ content: '\ea2c';
+}
+.web-icon-star:before {
+ content: '\ea2d';
+}
+.web-icon-system:before {
+ content: '\ea2e';
+}
+.web-icon-textmagic:before {
+ content: '\ea2f';
+}
+.web-icon-twitter:before {
+ content: '\ea30';
+}
+.web-icon-vue:before {
+ content: '\ea31';
+}
+.web-icon-x:before {
+ content: '\ea32';
+}
+.web-icon-ycombinator:before {
+ content: '\ea33';
+}
+.web-icon-youtube:before {
+ content: '\ea34';
+}
diff --git a/src/lib/UI/AuthorCover.svelte b/src/lib/UI/AuthorCover.svelte
new file mode 100644
index 0000000000..88a89ea2cf
--- /dev/null
+++ b/src/lib/UI/AuthorCover.svelte
@@ -0,0 +1,25 @@
+
+
+
+
+
@@ -101,7 +101,7 @@ display: flex; flex-direction: column; - gap: 3rem; + gap: 1rem; .info { h3 { diff --git a/src/lib/components/Article.svelte b/src/lib/components/Article.svelte index 9c46a44d46..18726d7d32 100644 --- a/src/lib/components/Article.svelte +++ b/src/lib/components/Article.svelte @@ -1,6 +1,6 @@