website-content-updates #7

Merged
caperren merged 36 commits from website-content-updates into main 2025-11-10 09:18:11 +00:00
21 changed files with 228 additions and 109 deletions
Showing only changes of commit c3673b76b6 - Show all commits

View File

@@ -1,12 +1,21 @@
// @ts-check // @ts-check
import { defineConfig } from 'astro/config'; import {defineConfig} from 'astro/config';
import sitemap from "@astrojs/sitemap";
import tailwindcss from "@tailwindcss/vite"; import tailwindcss from "@tailwindcss/vite";
import {siteLayout, getPaths} from "./src/data/site-layout.ts";
const disabledPaths = getPaths(siteLayout, [], true)
// https://astro.build/config // https://astro.build/config
export default defineConfig({ export default defineConfig({
integrations: [], site: "https://caperren.com",
integrations: [
sitemap({
filter: (pagePath) =>
!disabledPaths.some(disabledPath => pagePath.includes(disabledPath))
})
],
vite: { vite: {
plugins: [tailwindcss()], plugins: [tailwindcss()],
}, },

104
package-lock.json generated
View File

@@ -8,6 +8,8 @@
"name": "caperren-com", "name": "caperren-com",
"version": "0.0.1", "version": "0.0.1",
"dependencies": { "dependencies": {
"@astrojs/sitemap": "^3.6.0",
"@tailwindcss/vite": "^4.1.11",
"astro": "^5.15.4", "astro": "^5.15.4",
"flowbite": "^3.1.2", "flowbite": "^3.1.2",
"leader-line-new": "^1.1.9", "leader-line-new": "^1.1.9",
@@ -17,7 +19,6 @@
}, },
"devDependencies": { "devDependencies": {
"@playwright/test": "^1.56.1", "@playwright/test": "^1.56.1",
"@tailwindcss/vite": "^4.1.11",
"@types/luxon": "^3.7.1", "@types/luxon": "^3.7.1",
"@types/node": "^24.10.0", "@types/node": "^24.10.0",
"vitest": "^4.0.7" "vitest": "^4.0.7"
@@ -76,6 +77,17 @@
"node": "18.20.8 || ^20.3.0 || >=22.0.0" "node": "18.20.8 || ^20.3.0 || >=22.0.0"
} }
}, },
"node_modules/@astrojs/sitemap": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/@astrojs/sitemap/-/sitemap-3.6.0.tgz",
"integrity": "sha512-4aHkvcOZBWJigRmMIAJwRQXBS+ayoP5z40OklTXYXhUDhwusz+DyDl+nSshY6y9DvkVEavwNcFO8FD81iGhXjg==",
"license": "MIT",
"dependencies": {
"sitemap": "^8.0.0",
"stream-replace-string": "^2.0.0",
"zod": "^3.25.76"
}
},
"node_modules/@astrojs/telemetry": { "node_modules/@astrojs/telemetry": {
"version": "3.3.0", "version": "3.3.0",
"resolved": "https://registry.npmjs.org/@astrojs/telemetry/-/telemetry-3.3.0.tgz", "resolved": "https://registry.npmjs.org/@astrojs/telemetry/-/telemetry-3.3.0.tgz",
@@ -1048,7 +1060,6 @@
"version": "0.3.13", "version": "0.3.13",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
"integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/sourcemap-codec": "^1.5.0",
@@ -1059,7 +1070,6 @@
"version": "2.3.5", "version": "2.3.5",
"resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
"integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/gen-mapping": "^0.3.5",
@@ -1070,7 +1080,6 @@
"version": "3.1.2", "version": "3.1.2",
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
"dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=6.0.0" "node": ">=6.0.0"
@@ -1086,7 +1095,6 @@
"version": "0.3.31", "version": "0.3.31",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
"integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/resolve-uri": "^3.1.0",
@@ -1550,7 +1558,6 @@
"version": "4.1.17", "version": "4.1.17",
"resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.17.tgz", "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.17.tgz",
"integrity": "sha512-csIkHIgLb3JisEFQ0vxr2Y57GUNYh447C8xzwj89U/8fdW8LhProdxvnVH6U8M2Y73QKiTIH+LWbK3V2BBZsAg==", "integrity": "sha512-csIkHIgLb3JisEFQ0vxr2Y57GUNYh447C8xzwj89U/8fdW8LhProdxvnVH6U8M2Y73QKiTIH+LWbK3V2BBZsAg==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@jridgewell/remapping": "^2.3.4", "@jridgewell/remapping": "^2.3.4",
@@ -1566,7 +1573,6 @@
"version": "4.1.17", "version": "4.1.17",
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.17.tgz", "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.17.tgz",
"integrity": "sha512-F0F7d01fmkQhsTjXezGBLdrl1KresJTcI3DB8EkScCldyKp3Msz4hub4uyYaVnk88BAS1g5DQjjF6F5qczheLA==", "integrity": "sha512-F0F7d01fmkQhsTjXezGBLdrl1KresJTcI3DB8EkScCldyKp3Msz4hub4uyYaVnk88BAS1g5DQjjF6F5qczheLA==",
"dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">= 10" "node": ">= 10"
@@ -1593,7 +1599,6 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -1610,7 +1615,6 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -1627,7 +1631,6 @@
"cpu": [ "cpu": [
"x64" "x64"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -1644,7 +1647,6 @@
"cpu": [ "cpu": [
"x64" "x64"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -1661,7 +1663,6 @@
"cpu": [ "cpu": [
"arm" "arm"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -1678,7 +1679,6 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -1695,7 +1695,6 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -1712,7 +1711,6 @@
"cpu": [ "cpu": [
"x64" "x64"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -1729,7 +1727,6 @@
"cpu": [ "cpu": [
"x64" "x64"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -1754,7 +1751,6 @@
"cpu": [ "cpu": [
"wasm32" "wasm32"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"dependencies": { "dependencies": {
@@ -1776,7 +1772,6 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -1793,7 +1788,6 @@
"cpu": [ "cpu": [
"x64" "x64"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -1807,7 +1801,6 @@
"version": "4.1.17", "version": "4.1.17",
"resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.17.tgz", "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.17.tgz",
"integrity": "sha512-4+9w8ZHOiGnpcGI6z1TVVfWaX/koK7fKeSYF3qlYg2xpBtbteP2ddBxiarL+HVgfSJGeK5RIxRQmKm4rTJJAwA==", "integrity": "sha512-4+9w8ZHOiGnpcGI6z1TVVfWaX/koK7fKeSYF3qlYg2xpBtbteP2ddBxiarL+HVgfSJGeK5RIxRQmKm4rTJJAwA==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@tailwindcss/node": "4.1.17", "@tailwindcss/node": "4.1.17",
@@ -1915,6 +1908,15 @@
"integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==", "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/@types/sax": {
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/@types/sax/-/sax-1.2.7.tgz",
"integrity": "sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==",
"license": "MIT",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/unist": { "node_modules/@types/unist": {
"version": "3.0.3", "version": "3.0.3",
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz",
@@ -2149,6 +2151,12 @@
"url": "https://github.com/sponsors/jonschlinkert" "url": "https://github.com/sponsors/jonschlinkert"
} }
}, },
"node_modules/arg": {
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
"integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==",
"license": "MIT"
},
"node_modules/argparse": { "node_modules/argparse": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
@@ -2696,7 +2704,6 @@
"version": "2.1.2", "version": "2.1.2",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
"integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
"devOptional": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"engines": { "engines": {
"node": ">=8" "node": ">=8"
@@ -2773,7 +2780,6 @@
"version": "5.18.3", "version": "5.18.3",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz",
"integrity": "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==", "integrity": "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"graceful-fs": "^4.2.4", "graceful-fs": "^4.2.4",
@@ -3022,7 +3028,6 @@
"version": "4.2.11", "version": "4.2.11",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
"dev": true,
"license": "ISC" "license": "ISC"
}, },
"node_modules/h3": { "node_modules/h3": {
@@ -3376,7 +3381,6 @@
"version": "2.6.1", "version": "2.6.1",
"resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz",
"integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==",
"devOptional": true,
"license": "MIT", "license": "MIT",
"bin": { "bin": {
"jiti": "lib/jiti-cli.mjs" "jiti": "lib/jiti-cli.mjs"
@@ -3413,7 +3417,6 @@
"version": "1.30.2", "version": "1.30.2",
"resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.2.tgz", "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.2.tgz",
"integrity": "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==", "integrity": "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==",
"devOptional": true,
"license": "MPL-2.0", "license": "MPL-2.0",
"dependencies": { "dependencies": {
"detect-libc": "^2.0.3" "detect-libc": "^2.0.3"
@@ -3446,7 +3449,6 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"dev": true,
"license": "MPL-2.0", "license": "MPL-2.0",
"optional": true, "optional": true,
"os": [ "os": [
@@ -3467,7 +3469,6 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"dev": true,
"license": "MPL-2.0", "license": "MPL-2.0",
"optional": true, "optional": true,
"os": [ "os": [
@@ -3488,7 +3489,6 @@
"cpu": [ "cpu": [
"x64" "x64"
], ],
"dev": true,
"license": "MPL-2.0", "license": "MPL-2.0",
"optional": true, "optional": true,
"os": [ "os": [
@@ -3509,7 +3509,6 @@
"cpu": [ "cpu": [
"x64" "x64"
], ],
"dev": true,
"license": "MPL-2.0", "license": "MPL-2.0",
"optional": true, "optional": true,
"os": [ "os": [
@@ -3530,7 +3529,6 @@
"cpu": [ "cpu": [
"arm" "arm"
], ],
"dev": true,
"license": "MPL-2.0", "license": "MPL-2.0",
"optional": true, "optional": true,
"os": [ "os": [
@@ -3551,7 +3549,6 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"dev": true,
"license": "MPL-2.0", "license": "MPL-2.0",
"optional": true, "optional": true,
"os": [ "os": [
@@ -3572,7 +3569,6 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"dev": true,
"license": "MPL-2.0", "license": "MPL-2.0",
"optional": true, "optional": true,
"os": [ "os": [
@@ -3593,7 +3589,6 @@
"cpu": [ "cpu": [
"x64" "x64"
], ],
"dev": true,
"license": "MPL-2.0", "license": "MPL-2.0",
"optional": true, "optional": true,
"os": [ "os": [
@@ -3614,7 +3609,6 @@
"cpu": [ "cpu": [
"x64" "x64"
], ],
"dev": true,
"license": "MPL-2.0", "license": "MPL-2.0",
"optional": true, "optional": true,
"os": [ "os": [
@@ -3635,7 +3629,6 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"dev": true,
"license": "MPL-2.0", "license": "MPL-2.0",
"optional": true, "optional": true,
"os": [ "os": [
@@ -3656,7 +3649,6 @@
"cpu": [ "cpu": [
"x64" "x64"
], ],
"dev": true,
"license": "MPL-2.0", "license": "MPL-2.0",
"optional": true, "optional": true,
"os": [ "os": [
@@ -5159,6 +5151,12 @@
"fsevents": "~2.3.2" "fsevents": "~2.3.2"
} }
}, },
"node_modules/sax": {
"version": "1.4.3",
"resolved": "https://registry.npmjs.org/sax/-/sax-1.4.3.tgz",
"integrity": "sha512-yqYn1JhPczigF94DMS+shiDMjDowYO6y9+wB/4WgO0Y19jWYk0lQ4tuG5KI7kj4FTp1wxPj5IFfcrz/s1c3jjQ==",
"license": "BlueOak-1.0.0"
},
"node_modules/semver": { "node_modules/semver": {
"version": "7.7.3", "version": "7.7.3",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
@@ -5245,6 +5243,31 @@
"integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/sitemap": {
"version": "8.0.2",
"resolved": "https://registry.npmjs.org/sitemap/-/sitemap-8.0.2.tgz",
"integrity": "sha512-LwktpJcyZDoa0IL6KT++lQ53pbSrx2c9ge41/SeLTyqy2XUNA6uR4+P9u5IVo5lPeL2arAcOKn1aZAxoYbCKlQ==",
"license": "MIT",
"dependencies": {
"@types/node": "^17.0.5",
"@types/sax": "^1.2.1",
"arg": "^5.0.0",
"sax": "^1.4.1"
},
"bin": {
"sitemap": "dist/cli.js"
},
"engines": {
"node": ">=14.0.0",
"npm": ">=6.0.0"
}
},
"node_modules/sitemap/node_modules/@types/node": {
"version": "17.0.45",
"resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.45.tgz",
"integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==",
"license": "MIT"
},
"node_modules/smol-toml": { "node_modules/smol-toml": {
"version": "1.4.2", "version": "1.4.2",
"resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.4.2.tgz", "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.4.2.tgz",
@@ -5290,6 +5313,12 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/stream-replace-string": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/stream-replace-string/-/stream-replace-string-2.0.0.tgz",
"integrity": "sha512-TlnjJ1C0QrmxRNrON00JvaFFlNh5TTG00APw23j74ET7gkQpTASi6/L2fuiav8pzK715HXtUeClpBTw2NPSn6w==",
"license": "MIT"
},
"node_modules/string-width": { "node_modules/string-width": {
"version": "7.2.0", "version": "7.2.0",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
@@ -5358,7 +5387,6 @@
"version": "2.3.0", "version": "2.3.0",
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz",
"integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==",
"dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=6" "node": ">=6"
@@ -5840,7 +5868,6 @@
"version": "7.2.2", "version": "7.2.2",
"resolved": "https://registry.npmjs.org/vite/-/vite-7.2.2.tgz", "resolved": "https://registry.npmjs.org/vite/-/vite-7.2.2.tgz",
"integrity": "sha512-BxAKBWmIbrDgrokdGZH1IgkIk/5mMHDreLDmCJ0qpyJaAteP8NvMhkwr/ZCQNqNH97bw/dANTE9PDzqwJghfMQ==", "integrity": "sha512-BxAKBWmIbrDgrokdGZH1IgkIk/5mMHDreLDmCJ0qpyJaAteP8NvMhkwr/ZCQNqNH97bw/dANTE9PDzqwJghfMQ==",
"devOptional": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"esbuild": "^0.25.0", "esbuild": "^0.25.0",
@@ -5915,7 +5942,6 @@
"version": "2.3.3", "version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
"dev": true,
"hasInstallScript": true, "hasInstallScript": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,

View File

@@ -12,12 +12,13 @@
"e2e-test": "playwright test" "e2e-test": "playwright test"
}, },
"dependencies": { "dependencies": {
"@astrojs/sitemap": "^3.6.0",
"@tailwindcss/vite": "^4.1.11",
"astro": "^5.15.4", "astro": "^5.15.4",
"flowbite": "^3.1.2", "flowbite": "^3.1.2",
"leader-line-new": "^1.1.9", "leader-line-new": "^1.1.9",
"luxon": "^3.7.2", "luxon": "^3.7.2",
"tailwindcss": "^4.1.11", "tailwindcss": "^4.1.11",
"@tailwindcss/vite": "^4.1.11",
"uuid": "^13.0.0" "uuid": "^13.0.0"
}, },
"devDependencies": { "devDependencies": {

View File

@@ -15,14 +15,16 @@ const getHrefPath = (entry: navLink): string => {
<ul class={"flex flex-col p-4 bg-black border-caperren-green " + (depth ? "" : "md:flex-row md:space-x-8 md:mt-0")}> <ul class={"flex flex-col p-4 bg-black border-caperren-green " + (depth ? "" : "md:flex-row md:space-x-8 md:mt-0")}>
{ {
items.map((entry, index) => ( items.map((entry, index) => (
<li > (entry.enabled ?? true) && (
<li>
{Array.isArray(entry.children) && entry.children.length ? ( {Array.isArray(entry.children) && entry.children.length ? (
<button id={"dropdownNavbarLink" + getNavLinkSuffix(entry)} <button id={"dropdownNavbarLink" + getNavLinkSuffix(entry)}
data-dropdown-toggle={"dropdownNavbar" + getNavLinkSuffix(entry)} data-dropdown-toggle={"dropdownNavbar" + getNavLinkSuffix(entry)}
data-dropdown-placement="bottom" data-dropdown-placement="bottom"
class="flex items-center justify-between py-2 px-3 w-full hover:text-caperren-green-light md:hover:bg-transparent md:border-0 md:hover:text-caperren-green-light md:p-0 "> class="flex items-center justify-between py-2 px-3 w-full hover:text-caperren-green-light md:hover:bg-transparent md:border-0 md:hover:text-caperren-green-light md:p-0 ">
{entry.navText} {entry.navText}
<svg class="w-2.5 h-2.5 ms-2.5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" <svg class="w-2.5 h-2.5 ms-2.5" aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fill="none" viewBox="0 0 10 6"> fill="none" viewBox="0 0 10 6">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
stroke-width="2" stroke-width="2"
@@ -32,7 +34,8 @@ const getHrefPath = (entry: navLink): string => {
<div id={"dropdownNavbar" + getNavLinkSuffix(entry)} <div id={"dropdownNavbar" + getNavLinkSuffix(entry)}
class="z-10 hidden bg-black border border-caperren-green shadow-sm w-screen md:w-max"> class="z-10 hidden bg-black border border-caperren-green shadow-sm w-screen md:w-max">
<Astro.self items={entry.children} paths={[...paths, entry.path]} depth={depth + 1}/> <Astro.self items={entry.children} paths={[...paths, entry.path]}
depth={depth + 1}/>
</div> </div>
) : ( ) : (
@@ -43,5 +46,6 @@ const getHrefPath = (entry: navLink): string => {
)} )}
</li> </li>
)
))} ))}
</ul> </ul>

View File

@@ -30,6 +30,7 @@ export const siteLayout: navLink[] = [
path: "robotic-oceanographic-surface-sampler", path: "robotic-oceanographic-surface-sampler",
}, },
{ {
enabled: false,
navText: "LeConte Glacier Deployments", navText: "LeConte Glacier Deployments",
path: "leconte-glacier-deployments", path: "leconte-glacier-deployments",
} }
@@ -40,6 +41,7 @@ export const siteLayout: navLink[] = [
path: "osu-sinnhuber-aquatic-research-laboratory", path: "osu-sinnhuber-aquatic-research-laboratory",
children: [ children: [
{ {
enabled: false,
navText: "Team Lead", navText: "Team Lead",
path: "team-lead", path: "team-lead",
}, },
@@ -48,18 +50,22 @@ export const siteLayout: navLink[] = [
path: "zebrafish-embryo-pick-and-plate", path: "zebrafish-embryo-pick-and-plate",
}, },
{ {
enabled: false,
navText: "Shuttlebox Behavior System", navText: "Shuttlebox Behavior System",
path: "shuttlebox-behavior-system", path: "shuttlebox-behavior-system",
}, },
{ {
enabled: false,
navText: "Dechorionator", navText: "Dechorionator",
path: "dechorionator", path: "dechorionator",
}, },
{ {
enabled: false,
navText: "Denso Embryo Pick and Plate", navText: "Denso Embryo Pick and Plate",
path: "denso-embryo-pick-and-plate", path: "denso-embryo-pick-and-plate",
}, },
{ {
enabled: false,
navText: "ZScan Processor", navText: "ZScan Processor",
path: "zscan-processor", path: "zscan-processor",
} }
@@ -74,14 +80,17 @@ export const siteLayout: navLink[] = [
path: "mars-rover-software-team-lead", path: "mars-rover-software-team-lead",
}, },
{ {
enabled: false,
navText: "Mars Rover Emergency Software Team Lead", navText: "Mars Rover Emergency Software Team Lead",
path: "mars-rover-emergency-software-team-lead", path: "mars-rover-emergency-software-team-lead",
}, },
{ {
enabled: false,
navText: "Mars Rover Electrical Team Lead", navText: "Mars Rover Electrical Team Lead",
path: "mars-rover-electrical-team-lead", path: "mars-rover-electrical-team-lead",
}, },
{ {
enabled: false,
navText: "Club Officer", navText: "Club Officer",
path: "club-officer", path: "club-officer",
} }
@@ -95,12 +104,13 @@ export const siteLayout: navLink[] = [
children: [ children: [
{ {
enabled: false,
navText: "Homelab", path: "homelab", navText: "Homelab", path: "homelab",
children: [ children: [
{navText: "Home Server Rack", path: "home-server-rack"}, {enabled: false, navText: "Home Server Rack", path: "home-server-rack"},
{navText: "Offsite Backup Rack", path: "offsite-backup-rack"}, {enabled: false, navText: "Offsite Backup Rack", path: "offsite-backup-rack"},
{navText: "Kubernetes Cluster", path: "kubernetes-cluster"}, {enabled: false, navText: "Kubernetes Cluster", path: "kubernetes-cluster"},
{navText: "Home Automation", path: "home-automation"}, {enabled: false, navText: "Home Automation", path: "home-automation"},
] ]
}, },
{ {
@@ -109,30 +119,34 @@ export const siteLayout: navLink[] = [
children: [ children: [
{navText: "Lineup", path: "lineup"}, {navText: "Lineup", path: "lineup"},
{ {
enabled: false,
navText: "Custom Accessories", navText: "Custom Accessories",
path: "custom-accessories", path: "custom-accessories",
children: [ children: [
{navText: "Chubby Buttons 2 Mount", path: "chubby-buttons-2-mount"}, {navText: "Chubby Buttons 2 Mount", path: "chubby-buttons-2-mount"},
] ]
}, },
// { {
// navText: "Trips", enabled: false,
// path: "trips", navText: "Trips",
// children: [ path: "trips",
// {navText: "2025-08 | Alaska ", path: "2025-08-alaska"}, children: [
// {navText: "2024-10 | Norway ", path: "2024-10-norway"} {navText: "2025-08 | Alaska ", path: "2025-08-alaska", enabled: false,},
// ] {navText: "2024-10 | Norway ", path: "2024-10-norway", enabled: false,}
// },
] ]
}, },
// { ]
// title: "Projects", path: "projects", },
// children: [ {
// {title: "Shed Solar", path: "shed-solar"}, enabled: false,
// {title: "OSSM Overkill Edition", path: "ossm-overkill-edition"}, navText: "Projects",
// ] path: "projects",
// }, children: [
{navText: "NixOS", path: "nixos"}, {navText: "Shed Solar", path: "shed-solar", enabled: false},
{navText: "OSSM Overkill Edition", path: "ossm-overkill-edition", enabled: false},
]
},
{enabled: false, navText: "NixOS", path: "nixos"},
{navText: "Body Mods", path: "body-mods"}, {navText: "Body Mods", path: "body-mods"},
] ]
}, },
@@ -140,7 +154,7 @@ export const siteLayout: navLink[] = [
navText: "Resumes", navText: "Resumes",
path: "resume", path: "resume",
children: [ children: [
// {title: "2025-11-10 | Complete CV", path: "2025-11-10-complete-cv"}, {enabled: false, navText: "2025-11-10 | Complete CV", path: "2025-11-10-complete-cv"},
{navText: "2025-11-10 | Infrastructure Engineer", path: "2025-11-10-infrastructure-engineer"}, {navText: "2025-11-10 | Infrastructure Engineer", path: "2025-11-10-infrastructure-engineer"},
{navText: "2019-07-01 | Hardware Test Engineer", path: "2019-07-01-hardware-test-engineer"}, {navText: "2019-07-01 | Hardware Test Engineer", path: "2019-07-01-hardware-test-engineer"},
] ]
@@ -150,7 +164,13 @@ export const siteLayout: navLink[] = [
] ]
export const pathToMetadata = (path: string): navLink => { export const pathToMetadata = (path: string): navLink => {
const paths = path.split("/").filter((entry) => entry); let paths = path.split("/").filter((entry) => entry);
// Handle root path of /
if (paths.length < 1) {
paths = [""]
}
let currentEntries: navLink[] = siteLayout; let currentEntries: navLink[] = siteLayout;
let foundEntry: navLink | undefined; let foundEntry: navLink | undefined;
@@ -175,7 +195,8 @@ export const pathToMetadata = (path: string): navLink => {
export const getPaths = ( export const getPaths = (
currentEntries: navLink[] = siteLayout, currentEntries: navLink[] = siteLayout,
paths: string[] = [] paths: string[] = [],
disabledOnly = false
): string[] => { ): string[] => {
let foundPaths: string[] = []; let foundPaths: string[] = [];
@@ -183,11 +204,14 @@ export const getPaths = (
if (currentEntry.children && currentEntry.children.length > 0) { if (currentEntry.children && currentEntry.children.length > 0) {
foundPaths = [ foundPaths = [
...foundPaths, ...foundPaths,
...getPaths(currentEntry.children, [...paths, currentEntry.path || ""]) ...getPaths(currentEntry.children, [...paths, currentEntry.path || ""], disabledOnly)
] ]
} else { } else {
let enabled = currentEntry.enabled ?? true;
if (disabledOnly ? !enabled : enabled) {
foundPaths.push("/" + [...paths, currentEntry.path || ""].join("/")); foundPaths.push("/" + [...paths, currentEntry.path || ""].join("/"));
} }
} }
return foundPaths.filter(path => path !== "/"); }
return [...new Set(foundPaths)];
} }

View File

@@ -1,4 +1,5 @@
export interface navLink { export interface navLink {
enabled?: boolean;
navText: string; navText: string;
path?: string; path?: string;
pubpath?: string; pubpath?: string;

View File

@@ -4,26 +4,33 @@ import '@styles/global.css'
import Navbar from '@components/Navbar.astro'; import Navbar from '@components/Navbar.astro';
import Footer from '@components/Footer.astro'; import Footer from '@components/Footer.astro';
import {pathToMetadata} from "@data/site-layout.ts";
const pageTitle = Astro.props.title ? `${Astro.props.title} - Corwin Perren` : "Corwin Perren"; const pageTitle = Astro.props.title ? `${Astro.props.title} - Corwin Perren` : "Corwin Perren";
const showTitle = Astro.props.showTitle ?? true;
const pageEnabled = pathToMetadata(Astro.url.pathname).enabled ?? true;
--- ---
<!doctype html> <!doctype html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"/> <meta charset="UTF-8"/>
<link rel="icon" href="/favicon-solid.png" type="image/png"/> <link rel="icon" href="/favicon-solid.png" type="image/png"/>
<link rel="sitemap" href="/sitemap-index.xml"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/> <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>{pageTitle}</title> <title>{pageEnabled ? pageTitle : "Corwin Perren"}</title>
</head> </head>
<body class="bg-black text-white"> <body class="bg-black text-white">
<Navbar/> <Navbar/>
<main class="mx-6 mt-6 mb-14"> <main class="mx-6 mt-6 mb-14">
{(Astro.props.title) && ( {(Astro.props.title && showTitle && pageEnabled) && (
<h1 class="font-extrabold md:text-3xl md:mb-6">{Astro.props.title}</h1> <h1 class="font-extrabold md:text-3xl md:mb-6">{Astro.props.title}</h1>
)} )}
{pageEnabled && (
<slot/> <slot/>
)}
</main> </main>
<Footer/> <Footer/>
<script src="../scripts/main.ts"></script> <script src="../scripts/main.ts"></script>
</body> </body>
</html> </html>

View File

@@ -3,7 +3,7 @@ import BaseLayout from './BaseLayout.astro';
const resume = Astro.props.resume; const resume = Astro.props.resume;
--- ---
<BaseLayout> <BaseLayout title={Astro.props.title}>
<div class="h-dvh"> <div class="h-dvh">
<iframe src={resume} class="mx-auto w-9/10 md:w-3/5 h-7/10 md:h-4/5"/> <iframe src={resume} class="mx-auto w-9/10 md:w-3/5 h-7/10 md:h-4/5"/>
</div> </div>

View File

@@ -25,4 +25,32 @@ const rfidImplantCarouselGroup: carouselGroup = {
<HobbyLayout title="Body Mods"> <HobbyLayout title="Body Mods">
<h2 class="font-bold md:text-2xl my-4 underline">RFID Implant</h2> <h2 class="font-bold md:text-2xl my-4 underline">RFID Implant</h2>
<Carousel carouselGroup={rfidImplantCarouselGroup}/> <Carousel carouselGroup={rfidImplantCarouselGroup}/>
<p class="mt-4">
Back when I was in college, a few of my friends and I got this crazy idea to all get RFID implants together.
They are essentially the same things you'd use to microchip a pet, but with a slightly different firmware
configuration, allowing scans with any 125KHz-compatible reader. The implants came from <a
class="text-blue-500 hover:text-blue-300" href="https://dangerousthings.com/product/xem/">dangerousthings.com</a>,
and we were lucky enough to have a vet-med student as a friend who made the installation a quick and painless
process!
I'm glad that I'm not afraid of needles, as the 16 gauge injector the kit came with was nothing to scoff at.
Since healing, you would never know the implant was there, with the site leaving no scar or visible indication
of its presence.
</p>
<p class="mt-4">
With that out of the way, our group began work on hardware which would support the new implants.
The goal was to have a generic usb-keyboard emulator for typing passwords with a valid scan, a car
off-acc-on ignition replacement, and a fairly specialized modification to the OSU Robotics Club's doorway
scanning system so they would support these on top of the official OSU ID cards.
As tends to happen, life got busy, and only the usb-keyboard emulator actually came to fruition. The electronics
and
primary firmware were handled by <a
class="text-blue-500 hover:text-blue-300"
href="https://nickmccomb.net">Nick McComb</a>,
enclosure by
<a class="text-blue-500 hover:text-blue-300" href="https://dylanthrush.com">Dylan Thrush</a>, and I supported
some minor firmware development and debugging. If you want to see an example of the keyboard emulator unlocking
a PC, check out the video on <a
class="text-blue-500 hover:text-blue-300"
href="https://nickmccomb.net/college/printed-circuit-boards/computer-access-module">Nick's website</a>!
</p>
</HobbyLayout> </HobbyLayout>

View File

@@ -18,7 +18,7 @@ const headerCarouselGroup: carouselGroup = {
] ]
} }
--- ---
<BaseLayout> <BaseLayout title="About" showTitle={false}>
<Carousel carouselGroup={headerCarouselGroup}/> <Carousel carouselGroup={headerCarouselGroup}/>
<h2 class="font-bold md:text-2xl my-4 underline">Who Am I</h2> <h2 class="font-bold md:text-2xl my-4 underline">Who Am I</h2>
<p> <p>
@@ -56,7 +56,7 @@ const headerCarouselGroup: carouselGroup = {
href="/experience/spacex/avionics-test-engineering-internship">internship</a> at href="/experience/spacex/avionics-test-engineering-internship">internship</a> at
SpaceX in Hawthorne at the end of college, I applied for a <a class="text-blue-500 hover:text-blue-300" SpaceX in Hawthorne at the end of college, I applied for a <a class="text-blue-500 hover:text-blue-300"
href="/experience/spacex/hardware-test-engineer-i-ii">test href="/experience/spacex/hardware-test-engineer-i-ii">test
engineering</a> position for the company's Starlink team and was hired in mid-2019. engineering</a> position with the company's Starlink team and was hired in mid-2019.
For six years, I developed test system hardware, software, harnesses, mechanical fixtures, devops For six years, I developed test system hardware, software, harnesses, mechanical fixtures, devops
infrastructure, websites, and tooling to ensure that Starlink, Falcon, Dragon, and Starship component tests were infrastructure, websites, and tooling to ensure that Starlink, Falcon, Dragon, and Starship component tests were
producing well-validated and reliable hardware. producing well-validated and reliable hardware.

View File

@@ -3,4 +3,4 @@ import ResumeLayout from "@layouts/ResumeLayout.astro";
import resume from "@assets/resume/corwin_perren_2019-07-01_hardware_test_engineer.pdf" import resume from "@assets/resume/corwin_perren_2019-07-01_hardware_test_engineer.pdf"
--- ---
<ResumeLayout resume={resume}/> <ResumeLayout title="2019-07-01 - Hardware Test Engineer" resume={resume}/>

View File

@@ -1,6 +1,6 @@
--- ---
import ResumeLayout from "@layouts/ResumeLayout.astro"; import ResumeLayout from "@layouts/ResumeLayout.astro";
import resume from "@assets/resume/corwin_perren_2025_10_27_infrastructure_engineer.pdf" import resume from "@assets/resume/corwin_perren_2025-10-27-infrastructure_engineer.pdf"
--- ---
<ResumeLayout resume={resume}/> <ResumeLayout title="2025-10-27 - Infrastructure Engineer" resume={resume}/>

13
src/pages/robots.txt.ts Normal file
View File

@@ -0,0 +1,13 @@
import type { APIRoute } from 'astro';
const getRobotsTxt = (sitemapURL: URL) => `\
User-agent: *
Allow: /
Sitemap: ${sitemapURL.href}
`;
export const GET: APIRoute = ({ site }) => {
const sitemapURL = new URL('sitemap-index.xml', site);
return new Response(getRobotsTxt(sitemapURL));
};

View File

@@ -4,8 +4,14 @@ import {getPaths} from "@data/site-layout.ts";
for (const pagePath of getPaths()) { for (const pagePath of getPaths()) {
test(`${pagePath} Navigable`, async ({page}) => { test(`${pagePath}: Navigable`, async ({page}) => {
const response = await page.request.get(pagePath); const response = await page.request.get(pagePath);
await expect(response).toBeOK(); await expect(response).toBeOK();
}); });
test(`${pagePath}: Has Title`, async ({page}) => {
await page.goto(pagePath);
console.log(await page.title());
expect(await page.title()).not.toBe("Corwin Perren")
});
} }