diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 00000000..4a0a6c5f
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,5 @@
+*.png binary
+*.jpg binary
+*.jpeg binary
+*.avif binary
+*.webp binary
diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
index 0b411b37..15d51c3e 100644
--- a/.github/workflows/deploy.yml
+++ b/.github/workflows/deploy.yml
@@ -30,19 +30,19 @@ jobs:
- uses: actions/setup-node@v4
with:
- node-version: 18
+ node-version: 26
- - name: install pnpm
- run: npm i -g pnpm
+ - name: install node / pm
+ run: npm install -g @nubjs/nub
- name: Install Dependencies
- run: pnpm i
+ run: nub i
- name: Run Tests
- run: pnpm run test
+ run: nub run test
- name: Build Site
- run: pnpm run build
+ run: nub run build
- name: Merge MFCC Widget
run: |
diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml
index c5974231..e6eb773d 100644
--- a/.github/workflows/pr.yml
+++ b/.github/workflows/pr.yml
@@ -13,13 +13,13 @@ jobs:
- uses: actions/setup-node@v4
with:
- node-version: 18
+ node-version: 26
- - name: install pnpm
- run: npm i -g pnpm
+ - name: install node / pm
+ run: npm install -g @nubjs/nub
- name: Install Dependencies
- run: pnpm i
+ run: nub i
- name: Run Tests
run: pnpm run test
diff --git a/.nvmrc b/.nvmrc
index 0f9eb824..6f4247a6 100644
--- a/.nvmrc
+++ b/.nvmrc
@@ -1 +1 @@
-18.15
\ No newline at end of file
+26
diff --git a/package.json b/package.json
index b9d16578..682ad907 100644
--- a/package.json
+++ b/package.json
@@ -2,9 +2,12 @@
"name": "learn-2.0",
"version": "0.0.1",
"private": true,
+ "engines": {
+ "node": "^26.0.0"
+ },
"scripts": {
"dev": "node scripts/preprocess.js && vite dev",
- "build": "node scripts/preprocess.js && vite build",
+ "build": "node scripts/preprocess.js && node scripts/optimise-images.js && vite build",
"preview": "vite preview",
"check": "svelte-kit sync && svelte-check --tsconfig ./jsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./jsconfig.json --watch",
@@ -31,6 +34,7 @@
"prettier-plugin-svelte": "^2.10.1",
"rehype-autolink-headings": "^6.1.1",
"rehype-slug": "^5.1.0",
+ "sharp": "^0.35.3",
"svelte": "^4.2.12",
"svelte-check": "^3.6.4",
"svelte-preprocess": "^5.1.3",
@@ -63,5 +67,9 @@
"two.js": "^0.8.13",
"umap-js": "^1.3.3",
"waveform-data": "^4.5.0"
+ },
+ "allowScripts": {
+ "sharp@0.34.5": true,
+ "esbuild@0.21.5": true
}
-}
\ No newline at end of file
+}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index fd83acb5..9818946b 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -43,7 +43,7 @@ importers:
version: 4.17.21
meyda:
specifier: ^5.6.2
- version: 5.6.2
+ version: 5.6.2(supports-color@7.2.0)
numpy-parser:
specifier: ^1.2.3
version: 1.2.3
@@ -64,7 +64,7 @@ importers:
version: 5.8.3
svelte-youtube:
specifier: ^0.0.2
- version: 0.0.2(svelte@4.2.12)
+ version: 0.0.2(supports-color@7.2.0)(svelte@4.2.12)
tone:
specifier: ^15.0.4
version: 15.0.4
@@ -80,22 +80,22 @@ importers:
devDependencies:
'@sveltejs/adapter-static':
specifier: ^3.0.1
- version: 3.0.1(@sveltejs/kit@2.5.1(@sveltejs/vite-plugin-svelte@3.0.2(svelte@4.2.12)(vite@5.1.4(@types/node@20.11.20)))(svelte@4.2.12)(vite@5.1.4(@types/node@20.11.20)))
+ version: 3.0.1(@sveltejs/kit@2.5.1(@sveltejs/vite-plugin-svelte@3.0.2(supports-color@7.2.0)(svelte@4.2.12)(vite@5.1.4(@types/node@20.11.20)))(svelte@4.2.12)(vite@5.1.4(@types/node@20.11.20)))
'@sveltejs/kit':
specifier: ^2.5.1
- version: 2.5.1(@sveltejs/vite-plugin-svelte@3.0.2(svelte@4.2.12)(vite@5.1.4(@types/node@20.11.20)))(svelte@4.2.12)(vite@5.1.4(@types/node@20.11.20))
+ version: 2.5.1(@sveltejs/vite-plugin-svelte@3.0.2(supports-color@7.2.0)(svelte@4.2.12)(vite@5.1.4(@types/node@20.11.20)))(svelte@4.2.12)(vite@5.1.4(@types/node@20.11.20))
'@sveltejs/vite-plugin-svelte':
specifier: ^3.0.2
- version: 3.0.2(svelte@4.2.12)(vite@5.1.4(@types/node@20.11.20))
+ version: 3.0.2(supports-color@7.2.0)(svelte@4.2.12)(vite@5.1.4(@types/node@20.11.20))
eslint:
specifier: ^8.57.0
- version: 8.57.0
+ version: 8.57.0(supports-color@7.2.0)
eslint-config-prettier:
specifier: ^8.10.0
- version: 8.10.0(eslint@8.57.0)
+ version: 8.10.0(eslint@8.57.0(supports-color@7.2.0))
eslint-plugin-svelte3:
specifier: ^4.0.0
- version: 4.0.0(eslint@8.57.0)(svelte@4.2.12)
+ version: 4.0.0(eslint@8.57.0(supports-color@7.2.0))(svelte@4.2.12)
front-matter:
specifier: ^4.0.2
version: 4.0.2
@@ -129,6 +129,9 @@ importers:
rehype-slug:
specifier: ^5.1.0
version: 5.1.0
+ sharp:
+ specifier: ^0.35.3
+ version: 0.35.3(@types/node@20.11.20)
svelte:
specifier: ^4.2.12
version: 4.2.12
@@ -140,13 +143,13 @@ importers:
version: 5.1.3(postcss@8.4.35)(svelte@4.2.12)(typescript@5.3.3)
tap-mocha-reporter:
specifier: ^5.0.4
- version: 5.0.4
+ version: 5.0.4(supports-color@7.2.0)
vite:
specifier: ^5.1.4
version: 5.1.4(@types/node@20.11.20)
vitest:
specifier: ^1.3.1
- version: 1.3.1(@types/node@20.11.20)
+ version: 1.3.1(@types/node@20.11.20)(supports-color@7.2.0)
packages:
@@ -162,6 +165,9 @@ packages:
resolution: {integrity: sha512-VBj9MYyDb9tuLq7yzqjgzt6Q+IBQLrGZfdjOekyEirZPHxXWoTSGUTMrpsfi58Up73d13NfYLv8HT9vmznjzhQ==}
engines: {node: '>=6.9.0'}
+ '@emnapi/runtime@1.11.3':
+ resolution: {integrity: sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==}
+
'@esbuild/aix-ppc64@0.19.12':
resolution: {integrity: sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==}
engines: {node: '>=12'}
@@ -335,6 +341,7 @@ packages:
'@humanwhocodes/config-array@0.11.14':
resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==}
engines: {node: '>=10.10.0'}
+ deprecated: Use @eslint/config-array instead
'@humanwhocodes/module-importer@1.0.1':
resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==}
@@ -342,6 +349,169 @@ packages:
'@humanwhocodes/object-schema@2.0.2':
resolution: {integrity: sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==}
+ deprecated: Use @eslint/object-schema instead
+
+ '@img/colour@1.1.0':
+ resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==}
+ engines: {node: '>=18'}
+
+ '@img/sharp-darwin-arm64@0.35.3':
+ resolution: {integrity: sha512-RMnFX7YQsMoh7lWfcM4NEHHymBX/rLuKNPVM84XE9ONPcaSCDgE7CHIHpSgPcO2xcRthgBy1HfNO319mwhIAkg==}
+ engines: {node: '>=20.9.0'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@img/sharp-darwin-x64@0.35.3':
+ resolution: {integrity: sha512-Xo+5uFBtLN0BKqieTxiFzFPQAUlBbbH5iBKyRX/z1JrbnYsHTfKJnUfL8+p2TPXr1pXqao4eeL4Rl144uDpK9w==}
+ engines: {node: '>=20.9.0'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@img/sharp-freebsd-wasm32@0.35.3':
+ resolution: {integrity: sha512-lUxcqWIj2wMQ9BrwNjngcr1gWUr5xgaGThBRqPPalIC2n67Cqj1uPh8NnA/ZhAg8hUbKl+kVHKwgUIwe6ZYPrg==}
+ engines: {node: '>=20.9.0'}
+ os: [freebsd]
+
+ '@img/sharp-libvips-darwin-arm64@1.3.2':
+ resolution: {integrity: sha512-9J6ypZFpQBj4YnePGoq/S38w6nz+vqg5WZLrLGY4YuSemdMq47GMLBPO42MzwdGwpg/agZ7xzZcFHa48xlywfg==}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@img/sharp-libvips-darwin-x64@1.3.2':
+ resolution: {integrity: sha512-m2pW1n6cns9VaubNwsZ+c3CRYjxNQWgJ5gPlnL1nbBcpkBvFm6SCFN5o0psFHI8w9n11NKhFkeEDns98tiqbEw==}
+ cpu: [x64]
+ os: [darwin]
+
+ '@img/sharp-libvips-linux-arm64@1.3.2':
+ resolution: {integrity: sha512-dqVSFynCox4C/J8kT16V7SIFAns0IjgLwkvYT7p8LQVmJ5OS5b6tI9IGflxTeuBS//zXeFIUbwt5dwxyZ17cnA==}
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
+ '@img/sharp-libvips-linux-arm@1.3.2':
+ resolution: {integrity: sha512-1eMLzy92I4J6rmi4mAT8yC3HxOtniyGELlzGbNMLLeqe052ahFQ0h6LFq+lh5DsDIdYViIDst08abvSbcEdLXQ==}
+ cpu: [arm]
+ os: [linux]
+ libc: [glibc]
+
+ '@img/sharp-libvips-linux-ppc64@1.3.2':
+ resolution: {integrity: sha512-3z0NHDxD6n5I9gc05U1eW1AyRm+Gznzq3naMrthPNqE6oYykcogW0l/jfpJdjYnuNl8R7yI9pNbE1XiUeyq0Aw==}
+ cpu: [ppc64]
+ os: [linux]
+ libc: [glibc]
+
+ '@img/sharp-libvips-linux-riscv64@1.3.2':
+ resolution: {integrity: sha512-bsb4rI+NldGOsXuej2r8OdSS8+zXDVaCWxyWrcv6kneTOlgAHtZABRzBBCwdsPiD90J4myNJuHpg6kA20ImW/w==}
+ cpu: [riscv64]
+ os: [linux]
+ libc: [glibc]
+
+ '@img/sharp-libvips-linux-s390x@1.3.2':
+ resolution: {integrity: sha512-/ABshyj8gCpyIrNXnHn4LorDJ0HHm1VhXPBlxZ8zAtfVPAaSafXPGn+sUSIRiwaSBy0mmFjSjiXI5mkcwdChKQ==}
+ cpu: [s390x]
+ os: [linux]
+ libc: [glibc]
+
+ '@img/sharp-libvips-linux-x64@1.3.2':
+ resolution: {integrity: sha512-ITPEtgffGJ0S6G9dRyw/366tJQqFRcHWPHhC+Stpg3Z8AEMrDrTr2lhdz4f/Y/HMbRh//7Z5mBzEpVdi62Oc3w==}
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
+ '@img/sharp-libvips-linuxmusl-arm64@1.3.2':
+ resolution: {integrity: sha512-zE9EdiUzUmg5mDT5a1rk5fYJ6GWPloTwWBYDS14naqHsL+EaMpDj1AWnpLgh3u0YCORv2Tt50wrcrpYqkP97Kw==}
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
+ '@img/sharp-libvips-linuxmusl-x64@1.3.2':
+ resolution: {integrity: sha512-m0lrLiUt+lBYnCFr8qV/65yMR4E/c7/wf78I5eKTdkEakFAlZ9QlzEM3QIhhAwVeUhLAHLcCq7a7Vszq/oFNZQ==}
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
+ '@img/sharp-linux-arm64@0.35.3':
+ resolution: {integrity: sha512-QgKDspHPnrU+GQ55XPhGwyhC8acLVOOSyAvo1oVfFmrIXLkDNmGWzAfDZ4xK8oSA1qBQrALcHX0G5UZni/SuFQ==}
+ engines: {node: '>=20.9.0'}
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
+ '@img/sharp-linux-arm@0.35.3':
+ resolution: {integrity: sha512-affVWCTLooy8TSxbDx2qkzuDeaWLNVBA+P//FNBirHsXpP2fuBhk5AuboYUnrDnzoXes8GFjpTx0SBFOCRg+FA==}
+ engines: {node: '>=20.9.0'}
+ cpu: [arm]
+ os: [linux]
+ libc: [glibc]
+
+ '@img/sharp-linux-ppc64@0.35.3':
+ resolution: {integrity: sha512-sMd8rDxmpLOwv/7N44klFjOD5DUO7FLdjiXDI0hoxYaf7Ar262dQIEkosE98bps+5HPLtp/EvNqeqQtOycP/IA==}
+ engines: {node: '>=20.9.0'}
+ cpu: [ppc64]
+ os: [linux]
+ libc: [glibc]
+
+ '@img/sharp-linux-riscv64@0.35.3':
+ resolution: {integrity: sha512-0Eob78yjlYPfL5vMNWAW55l3R9Y6BQS/gOfe0ZcP9mEz9ohhKSt4im1hayiknXgf8AWrFqMvJcKIdmLmEe7yeQ==}
+ engines: {node: '>=20.9.0'}
+ cpu: [riscv64]
+ os: [linux]
+ libc: [glibc]
+
+ '@img/sharp-linux-s390x@0.35.3':
+ resolution: {integrity: sha512-KgAxQ0DxpNOq1rG2t5cgTgShJFGSuU7XO45cqC+1NVOuZnP6tlgZRuSYOfNupGkHID0o3cJOsw4DVeJpMovcGw==}
+ engines: {node: '>=20.9.0'}
+ cpu: [s390x]
+ os: [linux]
+ libc: [glibc]
+
+ '@img/sharp-linux-x64@0.35.3':
+ resolution: {integrity: sha512-8pqvxubL2PGdhlPy6GLqzDYMUjyRmKAwKHYKixpdJYBUK7PJ0C029XdsnpFIdgRZG68fZiGdHVWcKPvtiPB4cA==}
+ engines: {node: '>=20.9.0'}
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
+ '@img/sharp-linuxmusl-arm64@0.35.3':
+ resolution: {integrity: sha512-Vz0iQjzzcSX3HCbfwFfCSG/9SCIqyO0mH2sXyiHaAYfBk0cRsCWXRyQYX0ovCK/PAQBbTzQ0dsPQHh5MAFL59w==}
+ engines: {node: '>=20.9.0'}
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
+ '@img/sharp-linuxmusl-x64@0.35.3':
+ resolution: {integrity: sha512-6O1NPKcDVj9QEdg7Hx549EX8U0rp6yXQERqru6yRN7fGBn32UvIRJUlWnk+8xDCiG76hXVBbX82NZ/ZKr0euIg==}
+ engines: {node: '>=20.9.0'}
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
+ '@img/sharp-wasm32@0.35.3':
+ resolution: {integrity: sha512-cZ0XkcYGpHZkqW6iCkqTcmUC0CD9DhD5d/qeZlZkfRBn6GnHniZXLUo5+9xw8Iv76YE6LQFN9YNBlKREcCG76w==}
+ engines: {node: '>=20.9.0'}
+
+ '@img/sharp-webcontainers-wasm32@0.35.3':
+ resolution: {integrity: sha512-2rnq7bX3NzeR2T4YWgz8qiG4h3TSdMe+vN1iQXpJleSJ3SM5zQ8Fy2SyyXAWlbxpEZ2Y+Z4u1BePgJEYbSy80Q==}
+ engines: {node: '>=20.9.0'}
+ cpu: [wasm32]
+
+ '@img/sharp-win32-arm64@0.35.3':
+ resolution: {integrity: sha512-4bPwFdMbeC4JQ8L8LOyWp6nsHcboP5fxkp6iPOXz2Vg49R42TuMs2whkJ5OAP4/Ul035qOzy0AecOF9VOscn4w==}
+ engines: {node: '>=20.9.0'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@img/sharp-win32-ia32@0.35.3':
+ resolution: {integrity: sha512-r53mXsBN6lFUDiST764SvgwUdHAqM4rPAiDzAmf4fLoB6X/rkfyTrLCg6+g17wJJiCmB3JYgHuUldCWUIRFSXw==}
+ engines: {node: ^20.9.0}
+ cpu: [ia32]
+ os: [win32]
+
+ '@img/sharp-win32-x64@0.35.3':
+ resolution: {integrity: sha512-D4y1vNeZrIIJCN+uHaWVtH86B+aCrdMYYjicy9pXHvbGZeGYLLSd3wdVuC37FxVXlU1ARsk84eKWfWMXGYEqvA==}
+ engines: {node: '>=20.9.0'}
+ cpu: [x64]
+ os: [win32]
'@jest/schemas@29.6.3':
resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==}
@@ -423,26 +593,31 @@ packages:
resolution: {integrity: sha512-0fZBq27b+D7Ar5CQMofVN8sggOVhEtzFUwOwPppQt0k+VR+7UHMZZY4y+64WJ06XOhBTKXtQB/Sv0NwQMXyNAA==}
cpu: [arm64]
os: [linux]
+ libc: [glibc]
'@rollup/rollup-linux-arm64-musl@4.12.0':
resolution: {integrity: sha512-eTvzUS3hhhlgeAv6bfigekzWZjaEX9xP9HhxB0Dvrdbkk5w/b+1Sxct2ZuDxNJKzsRStSq1EaEkVSEe7A7ipgQ==}
cpu: [arm64]
os: [linux]
+ libc: [musl]
'@rollup/rollup-linux-riscv64-gnu@4.12.0':
resolution: {integrity: sha512-ix+qAB9qmrCRiaO71VFfY8rkiAZJL8zQRXveS27HS+pKdjwUfEhqo2+YF2oI+H/22Xsiski+qqwIBxVewLK7sw==}
cpu: [riscv64]
os: [linux]
+ libc: [glibc]
'@rollup/rollup-linux-x64-gnu@4.12.0':
resolution: {integrity: sha512-TenQhZVOtw/3qKOPa7d+QgkeM6xY0LtwzR8OplmyL5LrgTWIXpTQg2Q2ycBf8jm+SFW2Wt/DTn1gf7nFp3ssVA==}
cpu: [x64]
os: [linux]
+ libc: [glibc]
'@rollup/rollup-linux-x64-musl@4.12.0':
resolution: {integrity: sha512-LfFdRhNnW0zdMvdCb5FNuWlls2WbbSridJvxOvYWgSBOYZtgBfW9UGNJG//rwMqTX1xQE9BAodvMH9tAusKDUw==}
cpu: [x64]
os: [linux]
+ libc: [musl]
'@rollup/rollup-win32-arm64-msvc@4.12.0':
resolution: {integrity: sha512-JPDxovheWNp6d7AHCgsUlkuCKvtu3RB55iNEkaQcf0ttsDU/JZF+iQnYcQJSk/7PtT4mjjVG8N1kpwnI9SLYaw==}
@@ -562,6 +737,7 @@ packages:
'@ungap/structured-clone@1.2.0':
resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==}
+ deprecated: Potential CWE-502 - Update to 1.3.1 or higher
'@vitest/expect@1.3.1':
resolution: {integrity: sha512-xofQFwIzfdmLLlHa6ag0dPV8YsnKOCP1KdAeVVh34vSjN2dcUiXYCD9htu/9eM7t8Xln4v03U9HLxLpPlsXdZw==}
@@ -966,6 +1142,10 @@ packages:
resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==}
engines: {node: '>=8'}
+ detect-libc@2.1.2:
+ resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
+ engines: {node: '>=8'}
+
devalue@4.3.2:
resolution: {integrity: sha512-KqFl6pOgOW+Y6wJgu80rHpo2/3H07vr8ntR9rkkFIRETewbf5GaYYcakYfiKz89K+sLsuPkQIZaXDMjUObZwWg==}
@@ -1048,6 +1228,7 @@ packages:
eslint@8.57.0:
resolution: {integrity: sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+ deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options.
hasBin: true
esm-env@1.0.0:
@@ -1191,6 +1372,7 @@ packages:
glob@7.2.3:
resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
+ deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
globals@13.24.0:
resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==}
@@ -1261,6 +1443,7 @@ packages:
inflight@1.0.6:
resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==}
+ deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.
inherits@2.0.4:
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
@@ -1505,6 +1688,7 @@ packages:
node-domexception@1.0.0:
resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==}
engines: {node: '>=10.5.0'}
+ deprecated: Use your platform's native DOMException instead
node-fetch@2.6.13:
resolution: {integrity: sha512-StxNAxh15zr77QvvkmveSQ8uCQ4+v5FkvNTj0OESmiHu+VRi/gXArXtkWMElOsOUNLtUEvI4yS+rdtOHZTwlQA==}
@@ -1699,10 +1883,12 @@ packages:
rimraf@2.7.1:
resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==}
+ deprecated: Rimraf versions prior to v4 are no longer supported
hasBin: true
rimraf@3.0.2:
resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==}
+ deprecated: Rimraf versions prior to v4 are no longer supported
hasBin: true
robust-predicates@3.0.2:
@@ -1749,9 +1935,23 @@ packages:
engines: {node: '>=10'}
hasBin: true
+ semver@7.8.5:
+ resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==}
+ engines: {node: '>=10'}
+ hasBin: true
+
set-cookie-parser@2.6.0:
resolution: {integrity: sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==}
+ sharp@0.35.3:
+ resolution: {integrity: sha512-ej0zVHuZGHCiABXcNxeYhpRnPNPAcvbG8RMdBAhDAxLKkCRVSpK3Iyu7qbqw3JMzoj0REeM6f3tJLtVwl0023Q==}
+ engines: {node: '>=20.9.0'}
+ peerDependencies:
+ '@types/node': '*'
+ peerDependenciesMeta:
+ '@types/node':
+ optional: true
+
shebang-command@2.0.0:
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
engines: {node: '>=8'}
@@ -2175,6 +2375,11 @@ snapshots:
dependencies:
regenerator-runtime: 0.14.1
+ '@emnapi/runtime@1.11.3':
+ dependencies:
+ tslib: 2.7.0
+ optional: true
+
'@esbuild/aix-ppc64@0.19.12':
optional: true
@@ -2244,17 +2449,17 @@ snapshots:
'@esbuild/win32-x64@0.19.12':
optional: true
- '@eslint-community/eslint-utils@4.4.0(eslint@8.57.0)':
+ '@eslint-community/eslint-utils@4.4.0(eslint@8.57.0(supports-color@7.2.0))':
dependencies:
- eslint: 8.57.0
+ eslint: 8.57.0(supports-color@7.2.0)
eslint-visitor-keys: 3.4.3
'@eslint-community/regexpp@4.10.0': {}
- '@eslint/eslintrc@2.1.4':
+ '@eslint/eslintrc@2.1.4(supports-color@7.2.0)':
dependencies:
ajv: 6.12.6
- debug: 4.3.4
+ debug: 4.3.4(supports-color@7.2.0)
espree: 9.6.1
globals: 13.24.0
ignore: 5.3.1
@@ -2282,10 +2487,10 @@ snapshots:
dependencies:
'@tensorflow/tfjs': 4.17.0(seedrandom@3.0.5)
- '@humanwhocodes/config-array@0.11.14':
+ '@humanwhocodes/config-array@0.11.14(supports-color@7.2.0)':
dependencies:
'@humanwhocodes/object-schema': 2.0.2
- debug: 4.3.4
+ debug: 4.3.4(supports-color@7.2.0)
minimatch: 3.1.2
transitivePeerDependencies:
- supports-color
@@ -2294,6 +2499,112 @@ snapshots:
'@humanwhocodes/object-schema@2.0.2': {}
+ '@img/colour@1.1.0': {}
+
+ '@img/sharp-darwin-arm64@0.35.3':
+ optionalDependencies:
+ '@img/sharp-libvips-darwin-arm64': 1.3.2
+ optional: true
+
+ '@img/sharp-darwin-x64@0.35.3':
+ optionalDependencies:
+ '@img/sharp-libvips-darwin-x64': 1.3.2
+ optional: true
+
+ '@img/sharp-freebsd-wasm32@0.35.3':
+ dependencies:
+ '@img/sharp-wasm32': 0.35.3
+ optional: true
+
+ '@img/sharp-libvips-darwin-arm64@1.3.2':
+ optional: true
+
+ '@img/sharp-libvips-darwin-x64@1.3.2':
+ optional: true
+
+ '@img/sharp-libvips-linux-arm64@1.3.2':
+ optional: true
+
+ '@img/sharp-libvips-linux-arm@1.3.2':
+ optional: true
+
+ '@img/sharp-libvips-linux-ppc64@1.3.2':
+ optional: true
+
+ '@img/sharp-libvips-linux-riscv64@1.3.2':
+ optional: true
+
+ '@img/sharp-libvips-linux-s390x@1.3.2':
+ optional: true
+
+ '@img/sharp-libvips-linux-x64@1.3.2':
+ optional: true
+
+ '@img/sharp-libvips-linuxmusl-arm64@1.3.2':
+ optional: true
+
+ '@img/sharp-libvips-linuxmusl-x64@1.3.2':
+ optional: true
+
+ '@img/sharp-linux-arm64@0.35.3':
+ optionalDependencies:
+ '@img/sharp-libvips-linux-arm64': 1.3.2
+ optional: true
+
+ '@img/sharp-linux-arm@0.35.3':
+ optionalDependencies:
+ '@img/sharp-libvips-linux-arm': 1.3.2
+ optional: true
+
+ '@img/sharp-linux-ppc64@0.35.3':
+ optionalDependencies:
+ '@img/sharp-libvips-linux-ppc64': 1.3.2
+ optional: true
+
+ '@img/sharp-linux-riscv64@0.35.3':
+ optionalDependencies:
+ '@img/sharp-libvips-linux-riscv64': 1.3.2
+ optional: true
+
+ '@img/sharp-linux-s390x@0.35.3':
+ optionalDependencies:
+ '@img/sharp-libvips-linux-s390x': 1.3.2
+ optional: true
+
+ '@img/sharp-linux-x64@0.35.3':
+ optionalDependencies:
+ '@img/sharp-libvips-linux-x64': 1.3.2
+ optional: true
+
+ '@img/sharp-linuxmusl-arm64@0.35.3':
+ optionalDependencies:
+ '@img/sharp-libvips-linuxmusl-arm64': 1.3.2
+ optional: true
+
+ '@img/sharp-linuxmusl-x64@0.35.3':
+ optionalDependencies:
+ '@img/sharp-libvips-linuxmusl-x64': 1.3.2
+ optional: true
+
+ '@img/sharp-wasm32@0.35.3':
+ dependencies:
+ '@emnapi/runtime': 1.11.3
+ optional: true
+
+ '@img/sharp-webcontainers-wasm32@0.35.3':
+ dependencies:
+ '@img/sharp-wasm32': 0.35.3
+ optional: true
+
+ '@img/sharp-win32-arm64@0.35.3':
+ optional: true
+
+ '@img/sharp-win32-ia32@0.35.3':
+ optional: true
+
+ '@img/sharp-win32-x64@0.35.3':
+ optional: true
+
'@jest/schemas@29.6.3':
dependencies:
'@sinclair/typebox': 0.27.8
@@ -2400,13 +2711,13 @@ snapshots:
'@sinclair/typebox@0.27.8': {}
- '@sveltejs/adapter-static@3.0.1(@sveltejs/kit@2.5.1(@sveltejs/vite-plugin-svelte@3.0.2(svelte@4.2.12)(vite@5.1.4(@types/node@20.11.20)))(svelte@4.2.12)(vite@5.1.4(@types/node@20.11.20)))':
+ '@sveltejs/adapter-static@3.0.1(@sveltejs/kit@2.5.1(@sveltejs/vite-plugin-svelte@3.0.2(supports-color@7.2.0)(svelte@4.2.12)(vite@5.1.4(@types/node@20.11.20)))(svelte@4.2.12)(vite@5.1.4(@types/node@20.11.20)))':
dependencies:
- '@sveltejs/kit': 2.5.1(@sveltejs/vite-plugin-svelte@3.0.2(svelte@4.2.12)(vite@5.1.4(@types/node@20.11.20)))(svelte@4.2.12)(vite@5.1.4(@types/node@20.11.20))
+ '@sveltejs/kit': 2.5.1(@sveltejs/vite-plugin-svelte@3.0.2(supports-color@7.2.0)(svelte@4.2.12)(vite@5.1.4(@types/node@20.11.20)))(svelte@4.2.12)(vite@5.1.4(@types/node@20.11.20))
- '@sveltejs/kit@2.5.1(@sveltejs/vite-plugin-svelte@3.0.2(svelte@4.2.12)(vite@5.1.4(@types/node@20.11.20)))(svelte@4.2.12)(vite@5.1.4(@types/node@20.11.20))':
+ '@sveltejs/kit@2.5.1(@sveltejs/vite-plugin-svelte@3.0.2(supports-color@7.2.0)(svelte@4.2.12)(vite@5.1.4(@types/node@20.11.20)))(svelte@4.2.12)(vite@5.1.4(@types/node@20.11.20))':
dependencies:
- '@sveltejs/vite-plugin-svelte': 3.0.2(svelte@4.2.12)(vite@5.1.4(@types/node@20.11.20))
+ '@sveltejs/vite-plugin-svelte': 3.0.2(supports-color@7.2.0)(svelte@4.2.12)(vite@5.1.4(@types/node@20.11.20))
'@types/cookie': 0.6.0
cookie: 0.6.0
devalue: 4.3.2
@@ -2422,19 +2733,19 @@ snapshots:
tiny-glob: 0.2.9
vite: 5.1.4(@types/node@20.11.20)
- '@sveltejs/vite-plugin-svelte-inspector@2.0.0(@sveltejs/vite-plugin-svelte@3.0.2(svelte@4.2.12)(vite@5.1.4(@types/node@20.11.20)))(svelte@4.2.12)(vite@5.1.4(@types/node@20.11.20))':
+ '@sveltejs/vite-plugin-svelte-inspector@2.0.0(@sveltejs/vite-plugin-svelte@3.0.2(supports-color@7.2.0)(svelte@4.2.12)(vite@5.1.4(@types/node@20.11.20)))(supports-color@7.2.0)(svelte@4.2.12)(vite@5.1.4(@types/node@20.11.20))':
dependencies:
- '@sveltejs/vite-plugin-svelte': 3.0.2(svelte@4.2.12)(vite@5.1.4(@types/node@20.11.20))
- debug: 4.3.4
+ '@sveltejs/vite-plugin-svelte': 3.0.2(supports-color@7.2.0)(svelte@4.2.12)(vite@5.1.4(@types/node@20.11.20))
+ debug: 4.3.4(supports-color@7.2.0)
svelte: 4.2.12
vite: 5.1.4(@types/node@20.11.20)
transitivePeerDependencies:
- supports-color
- '@sveltejs/vite-plugin-svelte@3.0.2(svelte@4.2.12)(vite@5.1.4(@types/node@20.11.20))':
+ '@sveltejs/vite-plugin-svelte@3.0.2(supports-color@7.2.0)(svelte@4.2.12)(vite@5.1.4(@types/node@20.11.20))':
dependencies:
- '@sveltejs/vite-plugin-svelte-inspector': 2.0.0(@sveltejs/vite-plugin-svelte@3.0.2(svelte@4.2.12)(vite@5.1.4(@types/node@20.11.20)))(svelte@4.2.12)(vite@5.1.4(@types/node@20.11.20))
- debug: 4.3.4
+ '@sveltejs/vite-plugin-svelte-inspector': 2.0.0(@sveltejs/vite-plugin-svelte@3.0.2(supports-color@7.2.0)(svelte@4.2.12)(vite@5.1.4(@types/node@20.11.20)))(supports-color@7.2.0)(svelte@4.2.12)(vite@5.1.4(@types/node@20.11.20))
+ debug: 4.3.4(supports-color@7.2.0)
deepmerge: 4.3.1
kleur: 4.1.5
magic-string: 0.30.7
@@ -2944,13 +3255,17 @@ snapshots:
dct@0.1.0: {}
- debug@2.6.9:
+ debug@2.6.9(supports-color@7.2.0):
dependencies:
ms: 2.0.0
+ optionalDependencies:
+ supports-color: 7.2.0
- debug@4.3.4:
+ debug@4.3.4(supports-color@7.2.0):
dependencies:
ms: 2.1.2
+ optionalDependencies:
+ supports-color: 7.2.0
deep-eql@4.1.3:
dependencies:
@@ -2970,6 +3285,8 @@ snapshots:
detect-indent@6.1.0: {}
+ detect-libc@2.1.2: {}
+
devalue@4.3.2: {}
diff-sequences@29.6.3: {}
@@ -3041,13 +3358,13 @@ snapshots:
escape-string-regexp@4.0.0: {}
- eslint-config-prettier@8.10.0(eslint@8.57.0):
+ eslint-config-prettier@8.10.0(eslint@8.57.0(supports-color@7.2.0)):
dependencies:
- eslint: 8.57.0
+ eslint: 8.57.0(supports-color@7.2.0)
- eslint-plugin-svelte3@4.0.0(eslint@8.57.0)(svelte@4.2.12):
+ eslint-plugin-svelte3@4.0.0(eslint@8.57.0(supports-color@7.2.0))(svelte@4.2.12):
dependencies:
- eslint: 8.57.0
+ eslint: 8.57.0(supports-color@7.2.0)
svelte: 4.2.12
eslint-scope@7.2.2:
@@ -3057,20 +3374,20 @@ snapshots:
eslint-visitor-keys@3.4.3: {}
- eslint@8.57.0:
+ eslint@8.57.0(supports-color@7.2.0):
dependencies:
- '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0)
+ '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0(supports-color@7.2.0))
'@eslint-community/regexpp': 4.10.0
- '@eslint/eslintrc': 2.1.4
+ '@eslint/eslintrc': 2.1.4(supports-color@7.2.0)
'@eslint/js': 8.57.0
- '@humanwhocodes/config-array': 0.11.14
+ '@humanwhocodes/config-array': 0.11.14(supports-color@7.2.0)
'@humanwhocodes/module-importer': 1.0.1
'@nodelib/fs.walk': 1.2.8
'@ungap/structured-clone': 1.2.0
ajv: 6.12.6
chalk: 4.1.2
cross-spawn: 7.0.3
- debug: 4.3.4
+ debug: 4.3.4(supports-color@7.2.0)
doctrine: 3.0.0
escape-string-regexp: 4.0.0
eslint-scope: 7.2.2
@@ -3466,12 +3783,12 @@ snapshots:
merge2@1.4.1: {}
- meyda@5.6.2:
+ meyda@5.6.2(supports-color@7.2.0):
dependencies:
dct: 0.1.0
fftjs: 0.0.4
node-getopt: 0.3.2
- wav: 1.0.2
+ wav: 1.0.2(supports-color@7.2.0)
transitivePeerDependencies:
- supports-color
@@ -3803,8 +4120,43 @@ snapshots:
lru-cache: 6.0.0
optional: true
+ semver@7.8.5: {}
+
set-cookie-parser@2.6.0: {}
+ sharp@0.35.3(@types/node@20.11.20):
+ dependencies:
+ '@img/colour': 1.1.0
+ detect-libc: 2.1.2
+ semver: 7.8.5
+ optionalDependencies:
+ '@img/sharp-darwin-arm64': 0.35.3
+ '@img/sharp-darwin-x64': 0.35.3
+ '@img/sharp-freebsd-wasm32': 0.35.3
+ '@img/sharp-libvips-darwin-arm64': 1.3.2
+ '@img/sharp-libvips-darwin-x64': 1.3.2
+ '@img/sharp-libvips-linux-arm': 1.3.2
+ '@img/sharp-libvips-linux-arm64': 1.3.2
+ '@img/sharp-libvips-linux-ppc64': 1.3.2
+ '@img/sharp-libvips-linux-riscv64': 1.3.2
+ '@img/sharp-libvips-linux-s390x': 1.3.2
+ '@img/sharp-libvips-linux-x64': 1.3.2
+ '@img/sharp-libvips-linuxmusl-arm64': 1.3.2
+ '@img/sharp-libvips-linuxmusl-x64': 1.3.2
+ '@img/sharp-linux-arm': 0.35.3
+ '@img/sharp-linux-arm64': 0.35.3
+ '@img/sharp-linux-ppc64': 0.35.3
+ '@img/sharp-linux-riscv64': 0.35.3
+ '@img/sharp-linux-s390x': 0.35.3
+ '@img/sharp-linux-x64': 0.35.3
+ '@img/sharp-linuxmusl-arm64': 0.35.3
+ '@img/sharp-linuxmusl-x64': 0.35.3
+ '@img/sharp-webcontainers-wasm32': 0.35.3
+ '@img/sharp-win32-arm64': 0.35.3
+ '@img/sharp-win32-ia32': 0.35.3
+ '@img/sharp-win32-x64': 0.35.3
+ '@types/node': 20.11.20
+
shebang-command@2.0.0:
dependencies:
shebang-regex: 3.0.0
@@ -3846,9 +4198,9 @@ snapshots:
std-env@3.7.0: {}
- stream-parser@0.3.1:
+ stream-parser@0.3.1(supports-color@7.2.0):
dependencies:
- debug: 2.6.9
+ debug: 2.6.9(supports-color@7.2.0)
transitivePeerDependencies:
- supports-color
@@ -3933,10 +4285,10 @@ snapshots:
dependencies:
svelte-floating-ui: 1.5.8
- svelte-youtube@0.0.2(svelte@4.2.12):
+ svelte-youtube@0.0.2(supports-color@7.2.0)(svelte@4.2.12):
dependencies:
svelte: 4.2.12
- youtube-player: 5.6.0
+ youtube-player: 5.6.0(supports-color@7.2.0)
transitivePeerDependencies:
- supports-color
@@ -3957,10 +4309,10 @@ snapshots:
magic-string: 0.30.7
periscopic: 3.1.0
- tap-mocha-reporter@5.0.4:
+ tap-mocha-reporter@5.0.4(supports-color@7.2.0):
dependencies:
color-support: 1.1.3
- debug: 4.3.4
+ debug: 4.3.4(supports-color@7.2.0)
diff: 4.0.2
escape-string-regexp: 2.0.0
glob: 7.2.3
@@ -4104,10 +4456,10 @@ snapshots:
unist-util-stringify-position: 3.0.3
vfile-message: 3.1.4
- vite-node@1.3.1(@types/node@20.11.20):
+ vite-node@1.3.1(@types/node@20.11.20)(supports-color@7.2.0):
dependencies:
cac: 6.7.14
- debug: 4.3.4
+ debug: 4.3.4(supports-color@7.2.0)
pathe: 1.1.2
picocolors: 1.0.0
vite: 5.1.4(@types/node@20.11.20)
@@ -4134,7 +4486,7 @@ snapshots:
optionalDependencies:
vite: 5.1.4(@types/node@20.11.20)
- vitest@1.3.1(@types/node@20.11.20):
+ vitest@1.3.1(@types/node@20.11.20)(supports-color@7.2.0):
dependencies:
'@vitest/expect': 1.3.1
'@vitest/runner': 1.3.1
@@ -4143,7 +4495,7 @@ snapshots:
'@vitest/utils': 1.3.1
acorn-walk: 8.3.2
chai: 4.4.1
- debug: 4.3.4
+ debug: 4.3.4(supports-color@7.2.0)
execa: 8.0.1
local-pkg: 0.5.0
magic-string: 0.30.7
@@ -4154,7 +4506,7 @@ snapshots:
tinybench: 2.6.0
tinypool: 0.8.2
vite: 5.1.4(@types/node@20.11.20)
- vite-node: 1.3.1(@types/node@20.11.20)
+ vite-node: 1.3.1(@types/node@20.11.20)(supports-color@7.2.0)
why-is-node-running: 2.2.2
optionalDependencies:
'@types/node': 20.11.20
@@ -4167,13 +4519,13 @@ snapshots:
- supports-color
- terser
- wav@1.0.2:
+ wav@1.0.2(supports-color@7.2.0):
dependencies:
buffer-alloc: 1.2.0
buffer-from: 1.1.2
- debug: 2.6.9
+ debug: 2.6.9(supports-color@7.2.0)
readable-stream: 1.1.14
- stream-parser: 0.3.1
+ stream-parser: 0.3.1(supports-color@7.2.0)
transitivePeerDependencies:
- supports-color
@@ -4230,9 +4582,9 @@ snapshots:
yocto-queue@1.0.0: {}
- youtube-player@5.6.0:
+ youtube-player@5.6.0(supports-color@7.2.0):
dependencies:
- debug: 4.3.4
+ debug: 4.3.4(supports-color@7.2.0)
load-script: 1.0.0
sister: 3.0.2
transitivePeerDependencies:
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
new file mode 100644
index 00000000..0996ae77
--- /dev/null
+++ b/pnpm-workspace.yaml
@@ -0,0 +1,5 @@
+allowBuilds:
+ '@sveltejs/kit': true
+ core-js: true
+ esbuild: true
+ svelte-preprocess: true
diff --git a/scripts/optimise-images.js b/scripts/optimise-images.js
new file mode 100644
index 00000000..4fe89839
--- /dev/null
+++ b/scripts/optimise-images.js
@@ -0,0 +1,84 @@
+import fs from 'node:fs';
+import path from 'node:path';
+import sharp from 'sharp';
+
+const STATIC_DIR = 'static';
+const WIDTHS = [640, 960, 1280];
+const IMAGE_EXTS = new Set(['.png', '.jpg', '.jpeg', '.webp', '.gif']);
+const JPEG_OPTIONS = { quality: 88, mozjpeg: true };
+
+let total = 0;
+let skipped = 0;
+let generated = 0;
+
+function* walkImages(dir) {
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
+ for (const entry of entries) {
+ const full = path.join(dir, entry.name);
+ if (entry.isDirectory() && !entry.name.startsWith('.')) {
+ yield* walkImages(full);
+ } else if (entry.isFile() && IMAGE_EXTS.has(path.extname(entry.name).toLowerCase())) {
+ // Skip generated variants (e.g., img-480w.jpeg)
+ if (/-\d+w\.jpe?g$/i.test(entry.name)) continue;
+ yield full;
+ }
+ }
+}
+
+async function processImage(filePath) {
+ total++;
+
+ const ext = path.extname(filePath);
+ const baseName = filePath.slice(0, -ext.length);
+
+ // Get original dimensions
+ let metadata;
+ try {
+ metadata = await sharp(filePath).metadata();
+ } catch {
+ console.warn(` ⚠ Cannot read: ${filePath}`);
+ return;
+ }
+
+ const nativeW = metadata.width;
+ if (!nativeW) {
+ console.warn(` ⚠ No width metadata: ${filePath}`);
+ return;
+ }
+
+ for (const w of WIDTHS) {
+ // Don't upscale — clamp to native width if image is smaller than the target
+ const resizeW = Math.min(w, nativeW);
+
+ const jpegDest = `${baseName}-${w}w.jpeg`;
+
+ // Skip if already generated and source hasn't changed
+ const srcMtime = fs.statSync(filePath).mtimeMs;
+
+ if (fs.existsSync(jpegDest) && fs.statSync(jpegDest).mtimeMs > srcMtime) {
+ skipped++;
+ continue;
+ }
+
+ try {
+ const pipeline = sharp(filePath).resize(resizeW).withMetadata(false);
+
+ // Generate JPEG
+ await pipeline.clone().jpeg(JPEG_OPTIONS).toFile(jpegDest);
+
+ generated++;
+ } catch (err) {
+ console.warn(` ⚠ Failed ${filePath} @ ${w}w: ${err.message}`);
+ }
+ }
+}
+
+console.log('Optimizing images in static/ ...\n');
+
+for (const filePath of walkImages(STATIC_DIR)) {
+ const rel = path.relative(STATIC_DIR, filePath);
+ process.stdout.write(` ${rel.padEnd(60)}\r`);
+ await processImage(filePath);
+}
+
+console.log(`\nDone. ${total} images → ${generated} variants generated, ${skipped} skipped (up to date).\n`);
diff --git a/src/lib/components/Image.svelte b/src/lib/components/Image.svelte
index 3bf8bd6d..8543f496 100644
--- a/src/lib/components/Image.svelte
+++ b/src/lib/components/Image.svelte
@@ -1,23 +1,46 @@
-
(loaded = true)}
- class:loaded
- />
+
+
+
(loaded = true)}
+ class:loaded
+ />
+
{#if label}
@@ -49,6 +72,7 @@
img {
display: block;
max-width: 100%;
+ height: auto;
padding-top: var(--space-4);
padding-bottom: var(--space-4);
opacity: 0;
diff --git a/src/lib/components/cards/Article.svelte b/src/lib/components/cards/Article.svelte
index 4b80ef75..83b429de 100644
--- a/src/lib/components/cards/Article.svelte
+++ b/src/lib/components/cards/Article.svelte
@@ -4,6 +4,11 @@
import _ from 'lodash';
export let data = {};
+
+ $: featuredSrc = data.feature.featuredimage ||
+ _.sample(data.feature.images) ||
+ '/general/learn_default.jpeg';
+ $: featuredBase = featuredSrc.replace(/\.(png|jpe?g|webp|gif)$/i, '');
@@ -13,12 +18,15 @@
-
+
+
+
+
{data.blurb}
@@ -70,11 +78,9 @@
.card-image {
width: 100%;
- height: 10em;
+ height: 100%;
+ object-fit: cover;
border: 1px solid var(--color-neutral-200);
- background-position: center;
- background-repeat: no-repeat;
- background-size: cover;
border-radius: var(--radius-sm);
transition: transform 0.5s ease;
}
diff --git a/src/lib/components/mosaic/Article.svelte b/src/lib/components/mosaic/Article.svelte
index ada59ad2..8d77cd3e 100644
--- a/src/lib/components/mosaic/Article.svelte
+++ b/src/lib/components/mosaic/Article.svelte
@@ -1,5 +1,6 @@
-

+
+
+
+
{item.flair}
diff --git a/src/routes/(content)/get-started/Map.svelte b/src/routes/(content)/get-started/Map.svelte
index 87ef2fef..d79a3a38 100644
--- a/src/routes/(content)/get-started/Map.svelte
+++ b/src/routes/(content)/get-started/Map.svelte
@@ -1,6 +1,6 @@