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

104
package-lock.json generated
View File

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

View File

@@ -12,12 +12,13 @@
"e2e-test": "playwright test"
},
"dependencies": {
"@astrojs/sitemap": "^3.6.0",
"@tailwindcss/vite": "^4.1.11",
"astro": "^5.15.4",
"flowbite": "^3.1.2",
"leader-line-new": "^1.1.9",
"luxon": "^3.7.2",
"tailwindcss": "^4.1.11",
"@tailwindcss/vite": "^4.1.11",
"uuid": "^13.0.0"
},
"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")}>
{
items.map((entry, index) => (
<li >
(entry.enabled ?? true) && (
<li>
{Array.isArray(entry.children) && entry.children.length ? (
<button id={"dropdownNavbarLink" + getNavLinkSuffix(entry)}
data-dropdown-toggle={"dropdownNavbar" + getNavLinkSuffix(entry)}
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 ">
{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">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
stroke-width="2"
@@ -32,7 +34,8 @@ const getHrefPath = (entry: navLink): string => {
<div id={"dropdownNavbar" + getNavLinkSuffix(entry)}
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>
) : (
@@ -43,5 +46,6 @@ const getHrefPath = (entry: navLink): string => {
)}
</li>
)
))}
</ul>

View File

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

View File

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

View File

@@ -4,26 +4,33 @@ import '@styles/global.css'
import Navbar from '@components/Navbar.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 showTitle = Astro.props.showTitle ?? true;
const pageEnabled = pathToMetadata(Astro.url.pathname).enabled ?? true;
---
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<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"/>
<title>{pageTitle}</title>
<title>{pageEnabled ? pageTitle : "Corwin Perren"}</title>
</head>
<body class="bg-black text-white">
<Navbar/>
<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>
)}
{pageEnabled && (
<slot/>
)}
</main>
<Footer/>
<script src="../scripts/main.ts"></script>
</body>
</html>

View File

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

View File

@@ -25,4 +25,32 @@ const rfidImplantCarouselGroup: carouselGroup = {
<HobbyLayout title="Body Mods">
<h2 class="font-bold md:text-2xl my-4 underline">RFID Implant</h2>
<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>

View File

@@ -18,7 +18,7 @@ const headerCarouselGroup: carouselGroup = {
]
}
---
<BaseLayout>
<BaseLayout title="About" showTitle={false}>
<Carousel carouselGroup={headerCarouselGroup}/>
<h2 class="font-bold md:text-2xl my-4 underline">Who Am I</h2>
<p>
@@ -56,7 +56,7 @@ const headerCarouselGroup: carouselGroup = {
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"
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
infrastructure, websites, and tooling to ensure that Starlink, Falcon, Dragon, and Starship component tests were
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"
---
<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 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()) {
test(`${pagePath} Navigable`, async ({page}) => {
test(`${pagePath}: Navigable`, async ({page}) => {
const response = await page.request.get(pagePath);
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")
});
}