LeConte deployments page complete, better auto-formatting and import sorting, new inline link, popover definitions, and paragraph components, improvements to component interfaces
Some checks failed
Build and Test - Staging / test (pull_request) Failing after 2m31s
Build and Test - Staging / build_and_push (pull_request) Has been skipped
Build and Test - Staging / deploy_staging (pull_request) Has been skipped

This commit is contained in:
2025-12-05 01:02:18 -08:00
parent 91cd9af0f8
commit 3aa75e1a10
46 changed files with 617 additions and 76 deletions

View File

@@ -1,2 +1,7 @@
[**.{js,ts,astro}]
indent_size = 2
[*]
charset = utf-8
insert_final_newline = true
end_of_line = lf
indent_style = space
indent_size = 2
max_line_length = 80

View File

@@ -1,5 +1,23 @@
{
"plugins": ["prettier-plugin-astro", "prettier-plugin-tailwindcss"],
"astroOrganizeImportsMode": "All",
"importOrder": [
"^@core/(.*)$",
"^@server/(.*)$",
"^@ui/(.*)$",
"^@layouts/(.*)$",
"^@components/(.*)$",
"^@interfaces/(.*)$",
"^@assets/(.*)$",
"^[./]"
],
"importOrderSeparation": true,
"importOrderSortSpecifiers": true,
"plugins": [
"prettier-plugin-astro",
"prettier-plugin-tailwindcss",
"@trivago/prettier-plugin-sort-imports",
"prettier-plugin-astro-organize-imports"
],
"overrides": [
{
"files": "*.astro",

View File

@@ -13,7 +13,9 @@
spelling-add-new-words \
spelling-check \
cleanup-check \
cleanup-code
cleanup-code \
convert_video \
convert_video_times
default: dev
@@ -61,4 +63,29 @@ cleanup-check:
npx prettier . --check
cleanup-code:
npx prettier . --write
npx prettier . --write
convert_video:
ffmpeg \
-init_hw_device vaapi=va:/dev/dri/renderD128 \
-filter_hw_device va \
-i $(input) \
-vf 'format=nv12,hwupload,scale_vaapi=-2:720' \
-c:v h264_vaapi \
-rc_mode CQP \
-qp 28 \
-an \
$(extra_args) \
$(output)
convert_video_times:
ffmpeg \
-init_hw_device vaapi=va:/dev/dri/renderD128 \
-filter_hw_device va \
-i $(input) \
-vf 'format=nv12,hwupload,scale_vaapi=-2:720,trim=start=$(start):end=$(end)' \
-c:v h264_vaapi \
-rc_mode CQP \
-qp 28 \
-an \
$(output)

View File

@@ -1,11 +1,11 @@
// @ts-check
import { defineConfig } from "astro/config";
import sitemap from "@astrojs/sitemap";
import tailwindcss from "@tailwindcss/vite";
import { defineConfig } from "astro/config";
// We don't have access to short imports this early in the build chain
// noinspection ES6PreferShortImport
import { siteLayout, getPaths } from "./src/data/site-layout.ts";
import { getPaths, siteLayout } from "./src/data/site-layout.ts";
const disabledPaths = getPaths(siteLayout, [], true);

243
package-lock.json generated
View File

@@ -20,10 +20,12 @@
},
"devDependencies": {
"@playwright/test": "^1.56.1",
"@trivago/prettier-plugin-sort-imports": "^6.0.0",
"@types/luxon": "^3.7.1",
"@types/node": "^24.10.0",
"prettier": "3.7.3",
"prettier-plugin-astro": "0.14.1",
"prettier-plugin-astro-organize-imports": "^0.4.11",
"prettier-plugin-tailwindcss": "0.7.1",
"vitest": "^4.0.7"
}
@@ -110,6 +112,48 @@
"node": "18.20.8 || ^20.3.0 || >=22.0.0"
}
},
"node_modules/@babel/code-frame": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
"integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-validator-identifier": "^7.27.1",
"js-tokens": "^4.0.0",
"picocolors": "^1.1.1"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/generator": {
"version": "7.28.5",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz",
"integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/parser": "^7.28.5",
"@babel/types": "^7.28.5",
"@jridgewell/gen-mapping": "^0.3.12",
"@jridgewell/trace-mapping": "^0.3.28",
"jsesc": "^3.0.2"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-globals": {
"version": "7.28.0",
"resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz",
"integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-string-parser": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
@@ -143,6 +187,40 @@
"node": ">=6.0.0"
}
},
"node_modules/@babel/template": {
"version": "7.27.2",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz",
"integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.27.1",
"@babel/parser": "^7.27.2",
"@babel/types": "^7.27.1"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/traverse": {
"version": "7.28.5",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz",
"integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.27.1",
"@babel/generator": "^7.28.5",
"@babel/helper-globals": "^7.28.0",
"@babel/parser": "^7.28.5",
"@babel/template": "^7.27.2",
"@babel/types": "^7.28.5",
"debug": "^4.3.1"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/types": {
"version": "7.28.5",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz",
@@ -2342,6 +2420,47 @@
"vite": "^5.2.0 || ^6 || ^7"
}
},
"node_modules/@trivago/prettier-plugin-sort-imports": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/@trivago/prettier-plugin-sort-imports/-/prettier-plugin-sort-imports-6.0.0.tgz",
"integrity": "sha512-Xarx55ow0R8oC7ViL5fPmDsg1EBa1dVhyZFVbFXNtPPJyW2w9bJADIla8YFSaNG9N06XfcklA9O9vmw4noNxkQ==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"@babel/generator": "^7.28.0",
"@babel/parser": "^7.28.0",
"@babel/traverse": "^7.28.0",
"@babel/types": "^7.28.0",
"javascript-natural-sort": "^0.7.1",
"lodash-es": "^4.17.21",
"minimatch": "^9.0.0",
"parse-imports-exports": "^0.2.4"
},
"engines": {
"node": ">= 20"
},
"peerDependencies": {
"@vue/compiler-sfc": "3.x",
"prettier": "2.x - 3.x",
"prettier-plugin-ember-template-tag": ">= 2.0.0",
"prettier-plugin-svelte": "3.x",
"svelte": "4.x || 5.x"
},
"peerDependenciesMeta": {
"@vue/compiler-sfc": {
"optional": true
},
"prettier-plugin-ember-template-tag": {
"optional": true
},
"prettier-plugin-svelte": {
"optional": true
},
"svelte": {
"optional": true
}
}
},
"node_modules/@types/chai": {
"version": "5.2.3",
"resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz",
@@ -2922,6 +3041,13 @@
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"dev": true,
"license": "MIT"
},
"node_modules/base-64": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/base-64/-/base-64-1.0.0.tgz",
@@ -2976,6 +3102,16 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/brace-expansion": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
"integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"balanced-match": "^1.0.0"
}
},
"node_modules/brotli": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/brotli/-/brotli-1.3.3.tgz",
@@ -4419,6 +4555,13 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/javascript-natural-sort": {
"version": "0.7.1",
"resolved": "https://registry.npmjs.org/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz",
"integrity": "sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw==",
"dev": true,
"license": "MIT"
},
"node_modules/jiti": {
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz",
@@ -4428,6 +4571,13 @@
"jiti": "lib/jiti-cli.mjs"
}
},
"node_modules/js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
"dev": true,
"license": "MIT"
},
"node_modules/js-yaml": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
@@ -4440,6 +4590,19 @@
"js-yaml": "bin/js-yaml.js"
}
},
"node_modules/jsesc": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
"integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
"dev": true,
"license": "MIT",
"bin": {
"jsesc": "bin/jsesc"
},
"engines": {
"node": ">=6"
}
},
"node_modules/kleur": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz",
@@ -4704,6 +4867,13 @@
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lodash-es": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==",
"dev": true,
"license": "MIT"
},
"node_modules/longest-streak": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz",
@@ -5562,6 +5732,22 @@
"mini-svg-data-uri": "cli.js"
}
},
"node_modules/minimatch": {
"version": "9.0.5",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
"integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
"dev": true,
"license": "ISC",
"dependencies": {
"brace-expansion": "^2.0.1"
},
"engines": {
"node": ">=16 || 14 >=14.17"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/mrmime": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz",
@@ -5762,6 +5948,16 @@
"node": ">=8"
}
},
"node_modules/parse-imports-exports": {
"version": "0.2.4",
"resolved": "https://registry.npmjs.org/parse-imports-exports/-/parse-imports-exports-0.2.4.tgz",
"integrity": "sha512-4s6vd6dx1AotCx/RCI2m7t7GCh5bDRUtGNvRfHSP2wbBQdMi67pPe7mtzmgwcaQ8VKK/6IB7Glfyu3qdZJPybQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"parse-statements": "1.0.11"
}
},
"node_modules/parse-latin": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/parse-latin/-/parse-latin-7.0.0.tgz",
@@ -5780,6 +5976,13 @@
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/parse-statements": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/parse-statements/-/parse-statements-1.0.11.tgz",
"integrity": "sha512-HlsyYdMBnbPQ9Jr/VgJ1YF4scnldvJpJxCVx6KgqPL4dxppsWrJHCIIxQXMJrqGnsRkNPATbeMJ8Yxu7JMsYcA==",
"dev": true,
"license": "MIT"
},
"node_modules/parse5": {
"version": "7.3.0",
"resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz",
@@ -5920,6 +6123,45 @@
"node": "^14.15.0 || >=16.0.0"
}
},
"node_modules/prettier-plugin-astro-organize-imports": {
"version": "0.4.11",
"resolved": "https://registry.npmjs.org/prettier-plugin-astro-organize-imports/-/prettier-plugin-astro-organize-imports-0.4.11.tgz",
"integrity": "sha512-qJQZ7qgnTVmk8ALe1SXpCVGa2QNanhsKn6ivfGQqWH8SCgQY1/poxLVras0Q4fqRBugR3xPRGxovODk1tmkbwA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@astrojs/compiler": "^2.8.0",
"typescript": "^5.4.5"
},
"peerDependencies": {
"prettier": "^3.0",
"prettier-plugin-astro": "*",
"prettier-plugin-tailwindcss": "*"
},
"peerDependenciesMeta": {
"prettier-plugin-astro": {
"optional": true
},
"prettier-plugin-tailwindcss": {
"optional": true
}
}
},
"node_modules/prettier-plugin-sort-imports": {
"version": "1.8.9",
"resolved": "https://registry.npmjs.org/prettier-plugin-sort-imports/-/prettier-plugin-sort-imports-1.8.9.tgz",
"integrity": "sha512-DWGenlVAOqUZzlCpksqfmBMtgZAAofj6wV91rwoGh3ibVuVoOjxxTL3PnPzEzRru88AOUGShxi5RgTfSWpzMrA==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"prettier": "^3.1.1"
},
"peerDependencies": {
"typescript": ">4.0.0"
}
},
"node_modules/prettier-plugin-tailwindcss": {
"version": "0.7.1",
"resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.7.1.tgz",
@@ -6761,7 +7003,6 @@
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"license": "Apache-2.0",
"peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"

View File

@@ -1,7 +1,6 @@
{
"name": "caperren-com",
"type": "module",
"version": "0.0.1",
"scripts": {
"dev": "astro dev",
"dev-hosted": "astro dev --host",
@@ -15,7 +14,6 @@
"@astrojs/sitemap": "^3.6.0",
"@tailwindcss/vite": "^4.1.11",
"astro": "^5.16.3",
"cspell": "^9.3.2",
"flowbite": "^3.1.2",
"leader-line-new": "^1.1.9",
"luxon": "^3.7.2",
@@ -24,10 +22,13 @@
},
"devDependencies": {
"@playwright/test": "^1.56.1",
"@trivago/prettier-plugin-sort-imports": "^6.0.0",
"@types/luxon": "^3.7.1",
"@types/node": "^24.10.0",
"cspell": "^9.3.2",
"prettier": "3.7.3",
"prettier-plugin-astro": "0.14.1",
"prettier-plugin-astro-organize-imports": "^0.4.11",
"prettier-plugin-tailwindcss": "0.7.1",
"vitest": "^4.0.7"
}

View File

@@ -1,3 +1,4 @@
ADCP
ASSEM
astrojs
Candian
@@ -14,6 +15,8 @@ flowbite
HDFS
headshot
Homelab
hwupload
iceops
ITAR
Jetson
leconte
@@ -36,7 +39,13 @@ sinnhuber
sitemapindex
ssds
Starlink
steller
Steller
timelapse
trivago
Unstow
uuidv
vaapi
vitest
Zebrafish
zscan

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 MiB

View File

@@ -2,4 +2,4 @@
---
<h3 class="my-4 font-bold md:text-lg">{Astro.props.text}</h3>
<h3 class="mt-4 mb-2 font-bold md:text-lg">{Astro.props.text}</h3>

View File

@@ -0,0 +1,14 @@
---
interface Props {
href: string;
target?: string;
}
const { href, target = "_blank" } = Astro.props;
---
<>
<a class="text-blue-500 hover:text-blue-300" href={href} target={target}>
<slot />
</a>
</>

View File

@@ -1,11 +1,18 @@
---
interface Props {
title: string;
href: string;
target?: string;
}
const { title, href, target = "_blank" } = Astro.props;
---
<a
class="text-caperren-green border-caperren-green hover:border-caperren-green-light hover:text-caperren-green-light rounded-2xl border-2 bg-black p-2"
href={Astro.props.href}
target={Astro.props.target || "_blank"}
href={href}
target={target}
>
{Astro.props.title}
{title}
</a>

View File

@@ -1,7 +1,7 @@
import {
Carousel,
type CarouselItem,
type CarouselInterface,
type CarouselItem,
type CarouselOptions,
type IndicatorItem,
Modal,

View File

@@ -1,11 +1,34 @@
---
import { type videoConfig } from "@interfaces/video.ts";
interface Props {
videoPath: string;
videoType?: string;
const config: videoConfig = Astro.props.videoConfig;
console.log(config);
controls?: boolean;
autoPlay?: boolean;
loop?: boolean;
playsInline?: boolean;
}
const {
videoPath,
videoType = "video/mp4",
controls = true,
autoPlay = false,
loop = false,
playsInline = false,
} = Astro.props;
---
<video class="h-auto w-full max-w-1/2" controls>
<source src={config.videoPath} type={config.videoType ?? "video/mp4"} />
Your browser does not support the video tag.
</video>
<div class="mx-auto my-auto">
<video
class="h-auto w-full"
controls={controls}
autoplay={autoPlay}
loop={loop}
playsinline={playsInline}
>
<source src={videoPath} type={videoType} />
Your browser does not support the video tag.
</video>
</div>

View File

@@ -0,0 +1,7 @@
---
---
<div class="">
<slot />
</div>

View File

@@ -0,0 +1,7 @@
---
---
<div class="space-y-2">
<slot />
</div>

View File

@@ -0,0 +1,43 @@
---
import { v4 as uuidv4 } from "uuid";
const popoverUUID = uuidv4();
const keys: { [key: string]: string } = {
ADCP: "Acoustic doppler current profiler",
COTS: "Consumer off-the-shelf",
CTD: "Conductivity, temperature, and depth sensor",
};
const key: string | undefined = Astro.props.key;
let word: string | undefined = Astro.props.word;
let definition: string | undefined = Astro.props.definition;
if (key && keys.hasOwnProperty(key)) {
word = key;
definition = keys[key];
}
---
<>
<a
href="#"
class="text-fg-brand decoration-caperren-green font-medium underline decoration-dashed hover:no-underline"
data-popover-target={popoverUUID}>{word}</a
>
<div
data-popover
id={popoverUUID}
role="tooltip"
class="text-body border-caperren-green invisible absolute z-90 inline-block w-fit max-w-96 rounded-lg border border-dashed bg-black p-3 text-sm opacity-0 shadow-xs transition-opacity duration-300"
>
<div>
<h3
class="text-heading border-b-caperren-green-dark mb-1 border-b font-semibold"
>
{word}
</h3>
<p>{definition}</p>
</div>
</div>
</>

View File

@@ -8,14 +8,17 @@ export const siteLayout: navLink[] = [
path: "experience",
children: [
{
enabled: false,
navText: "SpaceX",
path: "spacex",
children: [
{
enabled: false,
navText: "Hardware Test Engineer I/II",
path: "hardware-test-engineer-i-ii",
},
{
enabled: false,
navText: "Avionics Test Engineering Internship",
path: "avionics-test-engineering-internship",
},
@@ -26,6 +29,7 @@ export const siteLayout: navLink[] = [
path: "osu-ceoas-ocean-mixing-group",
children: [
{
enabled: false,
navText: "Robotics Oceanographic Surface Sampler",
path: "robotic-oceanographic-surface-sampler",
},

View File

@@ -1,15 +1,23 @@
---
import "@styles/global.css";
import Navbar from "@components/Navbar.astro";
import Footer from "@components/Footer.astro";
import Navbar from "@components/Navbar.astro";
import { pathToMetadata } from "@data/site-layout.ts";
interface Props {
title?: string;
subTitles?: string[];
showTitle?: boolean;
}
const { title, subTitles, showTitle = true } = Astro.props;
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;
---
@@ -38,12 +46,31 @@ const pageEnabled = pathToMetadata(Astro.url.pathname).enabled ?? true;
<Navbar />
<main class="mx-6 my-6">
{
Astro.props.title && showTitle && pageEnabled && (
<h1 class="font-extrabold md:mb-6 md:text-3xl">
{Astro.props.title}
title && showTitle && pageEnabled && (
<h1
class={
"text-xl font-extrabold md:text-3xl " +
(subTitles ? "" : "md:mb-6")
}
>
{title}
</h1>
)
}
{
showTitle &&
pageEnabled &&
subTitles?.map((subTitle, index) => (
<p
class={
"text-sm font-bold md:text-xl " +
(index == subTitles.length - 1 ? "mb-2 md:mb-6" : "")
}
>
{subTitle}
</p>
))
}
{pageEnabled && <slot />}
</main>
</div>

View File

@@ -2,6 +2,6 @@
import BaseLayout from "./BaseLayout.astro";
---
<BaseLayout title={Astro.props.title}>
<BaseLayout {...Astro.props}>
<slot />
</BaseLayout>

View File

@@ -2,6 +2,6 @@
import BaseLayout from "./BaseLayout.astro";
---
<BaseLayout title={Astro.props.title}>
<BaseLayout {...Astro.props}>
<slot />
</BaseLayout>

View File

@@ -1,9 +1,9 @@
---
import BaseLayout from "@layouts/BaseLayout.astro";
import PdfViewer from "@components/Media/PdfViewer.astro";
import BaseLayout from "@layouts/BaseLayout.astro";
---
<BaseLayout title={Astro.props.title}>
<BaseLayout {...Astro.props}>
<div class="h-dvh">
<PdfViewer class="mx-auto" pdf={Astro.props.resume} />
</div>

View File

@@ -1,8 +1,8 @@
---
import BaseLayout from "@layouts/BaseLayout.astro";
import Carousel from "@components/Media/CustomCarousel/CustomCarousel.astro";
import Timeline from "@components/Timeline/Timeline.astro";
import Table from "@components/Table.astro";
import Timeline from "@components/Timeline/Timeline.astro";
import BaseLayout from "@layouts/BaseLayout.astro";
import type { carouselGroup } from "@interfaces/image-carousel.ts";
import type { tableData } from "@interfaces/table.ts";

View File

@@ -1,12 +1,15 @@
---
import ExperienceLayout from "@layouts/ExperienceLayout.astro";
import H3 from "@components/CustomHtmlWrappers/H3.astro";
import Timeline from "@components/Timeline/Timeline.astro";
import Carousel from "@components/Media/CustomCarousel/CustomCarousel.astro";
import H2 from "@components/CustomHtmlWrappers/H2.astro";
import H3 from "@components/CustomHtmlWrappers/H3.astro";
import Carousel from "@components/Media/CustomCarousel/CustomCarousel.astro";
import Paragraph from "@components/Paragraph.astro";
import Timeline from "@components/Timeline/Timeline.astro";
import type { carouselGroup } from "@interfaces/image-carousel.ts";
import { deploymentTimeline } from "./osu-ceoas-ocean-mixing-group.ts";
import type { carouselGroup } from "@interfaces/image-carousel.ts";
import building from "@assets/experience/osu-ceoas-ocean-mixing-group/leconte-glacier-deployments/building.jpg";
import glacier_selfie from "@assets/experience/osu-ceoas-ocean-mixing-group/leconte-glacier-deployments/glacier-selfie.jpg";
@@ -15,16 +18,34 @@ import iced_in from "@assets/experience/osu-ceoas-ocean-mixing-group/leconte-gla
import pushing_icebergs from "@assets/experience/osu-ceoas-ocean-mixing-group/leconte-glacier-deployments/pushing-icebergs.jpg";
import ross_at_terminus from "@assets/experience/osu-ceoas-ocean-mixing-group/leconte-glacier-deployments/ross-at-terminus.png";
import ross_on_the_docks from "@assets/experience/osu-ceoas-ocean-mixing-group/leconte-glacier-deployments/ross-on-the-docks.jpg";
import ross_steller_ice_operations from "@assets/experience/osu-ceoas-ocean-mixing-group/leconte-glacier-deployments/ross-steller-ice-operations.png";
import steller_at_terminus from "@assets/experience/osu-ceoas-ocean-mixing-group/leconte-glacier-deployments/steller-at-terminus.png";
import steller_in_ice_from_above from "@assets/experience/osu-ceoas-ocean-mixing-group/leconte-glacier-deployments/steller-in-ice-from-above.jpg";
import whole_glacier from "@assets/experience/osu-ceoas-ocean-mixing-group/leconte-glacier-deployments/whole-glacier.jpg";
import working_at_the_docks from "@assets/experience/osu-ceoas-ocean-mixing-group/leconte-glacier-deployments/working-at-the-docks.jpg";
import working_trailer from "@assets/experience/osu-ceoas-ocean-mixing-group/leconte-glacier-deployments/working-trailer.jpg";
import massive_calving from "@assets/experience/osu-ceoas-ocean-mixing-group/leconte-glacier-deployments/videos/massive-calving-compressed.mp4";
import petersburg_working_timelapse from "@assets/experience/osu-ceoas-ocean-mixing-group/leconte-glacier-deployments/videos/petersburg-working-timelapse-compressed.mp4";
import ross_iceops from "@assets/experience/osu-ceoas-ocean-mixing-group/leconte-glacier-deployments/videos/ross-iceops-compressed.mp4";
import ross_terminus_calving from "@assets/experience/osu-ceoas-ocean-mixing-group/leconte-glacier-deployments/videos/ross-terminus-calving-compressed.mp4";
const videos = [
ross_terminus_calving,
ross_iceops,
massive_calving,
petersburg_working_timelapse,
];
const headerCarouselGroup: carouselGroup = {
animation: "slide",
images: [
ross_at_terminus,
ground_station,
ross_steller_ice_operations,
steller_at_terminus,
steller_in_ice_from_above,
whole_glacier,
ground_station,
glacier_selfie,
iced_in,
pushing_icebergs,
@@ -34,12 +55,84 @@ const headerCarouselGroup: carouselGroup = {
working_trailer,
],
};
import InlineLink from "@components/InlineLink.astro";
import Video from "@components/Media/Video.astro";
import Paragraphs from "@components/Paragraphs.astro";
import PopoverWordDefinition from "@components/PopoverWordDefinition.astro";
import { subTitles } from "./osu-ceoas-ocean-mixing-group.ts";
---
<ExperienceLayout title="CEOAS - LeConte Glacier Deployments">
<ExperienceLayout title="LeConte Glacier Deployments" subTitles={subTitles}>
<Carousel carouselGroup={headerCarouselGroup} />
<H2 text="Summary" />
<H3 text="Timeline" />
<Timeline timeline={deploymentTimeline} />
<H3 text="Details" />
<H3 text="Location" />
<iframe
class="w-full"
width="600"
height="450"
src="https://maps.google.com/maps?q=leconte%20glacier&t=k&z=11&ie=UTF8&iwloc=B&output=embed"
></iframe>
<H2 text="Details" />
<Paragraphs>
<Paragraph>
As part of my time working on the
<InlineLink
href="/experience/osu-ceoas-ocean-mixing-group/robotic-oceanographic-surface-sampler"
>
Robotic Oceanographic Surface Sampler</InlineLink
>, I had the fantastic opportunity to be deployed at the LeConte Glacier
in Alaska! This started in early 2017 with setup and ocean trials in
nearby Petersburg. The team had sent multiple shipping containers with our
robotic platforms and most equipment to assemble, test, and debug them a
few months prior, allowing us to get to work the moment we arrived. We
spent multiple weeks at the docks with our makeshift workstations built
from plywood and pelican cases, validating the hardware we'd sent, and
making adjustments with improved hardware we'd hand-carried on our
flights. This also provided a great opportunity to work out any final
firmware and/or software bugs while the vehicles were still relatively
easy to retrieve. After a short trip back home to recover, and prep any
last minute items we'd forgotten, our research team flew back and headed
for the glacier!
</Paragraph>
<Paragraph>
The towering mountain of ice sits roughly 30 miles from Petersburg, so
we'd commissioned an off-season fishing vessel, Steller, and it's crew, to
take us as close to it as was reasonably safe. The team worked 24 hours a
day, on two shifts, deploying and retrieving the ROSS platforms,
performing repairs (as needed), recovering/processing collected data,
manually deploying the ship's <PopoverWordDefinition key="CTD" />, and
occasionally spending considerable time pushing icebergs the size of
houses away from an <PopoverWordDefinition key="ADCP" /> mounted to Steller
using fiberglass poles. Many hardware failures had to be solved during these
long days, and it was a very rewarding and creative experience to work around
the limitations of this isolated (and salty) environment.
</Paragraph>
<Paragraph>
On top of being a unique engineering and team building experience, LeConte
lives among the most beautiful places I've yet to experience in this life.
There's something special about being somewhere so incredibly remote and
untouched by humans. The pristine evergreen forests, eerie blue-green hues
of the glacier and icebergs, ancient towering mountains, and genuinely
curious looks from local land and marine life unfamiliar with human
presence made it humbly clear that for once we as humans were the odd
ones out. These trips were ones that I will treasure and think back on
fondly on for the rest of my life.
</Paragraph>
</Paragraphs>
<H2 text="Videos" />
<div class="grid grid-cols-1 gap-4 md:grid-cols-2">
{
videos.map((video) => (
<Video
videoPath={video}
autoPlay={true}
loop={true}
playsInline={true}
/>
))
}
</div>
</ExperienceLayout>

View File

@@ -22,3 +22,8 @@ export const deploymentTimeline: timelineEntry[] = [
date: "September 2017",
},
];
export const subTitles = [
"Oregon State University",
"College of Earth, Ocean, and Atmospheric Sciences",
];

View File

@@ -1,26 +1,29 @@
---
import ExperienceLayout from "@layouts/ExperienceLayout.astro";
import H2 from "@components/CustomHtmlWrappers/H2.astro";
import H3 from "@components/CustomHtmlWrappers/H3.astro";
import LinkButton from "@components/LinkButton.astro";
import Carousel from "@components/Media/CustomCarousel/CustomCarousel.astro";
import PdfViewer from "@components/Media/PdfViewer.astro";
import PopoverWordDefinition from "@components/PopoverWordDefinition.astro";
import Timeline from "@components/Timeline/Timeline.astro";
import type { carouselGroup } from "@interfaces/image-carousel.ts";
import type { timelineEntry } from "@interfaces/timeline.ts";
import Carousel from "@components/Media/CustomCarousel/CustomCarousel.astro";
import H2 from "@components/CustomHtmlWrappers/H2.astro";
import H3 from "@components/CustomHtmlWrappers/H3.astro";
import LinkButton from "@components/LinkButton.astro";
import PdfViewer from "@components/Media/PdfViewer.astro";
import Timeline from "@components/Timeline/Timeline.astro";
import publication from "@assets/experience/osu-ceoas-ocean-mixing-group/robotic-oceanographic-surface-sampler/ross-publication.pdf";
import electronics_box from "@assets/experience/osu-ceoas-ocean-mixing-group/robotic-oceanographic-surface-sampler/electronics-box.jpg";
import jet_drive from "@assets/experience/osu-ceoas-ocean-mixing-group/robotic-oceanographic-surface-sampler/jet-drive.jpg";
import ross_on_vessel from "@assets/experience/osu-ceoas-ocean-mixing-group/robotic-oceanographic-surface-sampler/ross-on-vessel.jpg";
import ross_on_vessel_at_night from "@assets/experience/osu-ceoas-ocean-mixing-group/robotic-oceanographic-surface-sampler/ross-on-vessel-at-night.jpg";
import ross_on_vessel from "@assets/experience/osu-ceoas-ocean-mixing-group/robotic-oceanographic-surface-sampler/ross-on-vessel.jpg";
import publication from "@assets/experience/osu-ceoas-ocean-mixing-group/robotic-oceanographic-surface-sampler/ross-publication.pdf";
import ross_team from "@assets/experience/osu-ceoas-ocean-mixing-group/robotic-oceanographic-surface-sampler/ross-team.jpg";
import ui from "@assets/experience/osu-ceoas-ocean-mixing-group/robotic-oceanographic-surface-sampler/ui.jpg";
import { deploymentTimeline } from "./osu-ceoas-ocean-mixing-group.ts";
import {
deploymentTimeline,
subTitles,
} from "./osu-ceoas-ocean-mixing-group.ts";
const headerCarouselGroup: carouselGroup = {
animation: "slide",
@@ -49,7 +52,10 @@ const timeline: timelineEntry[] = [
];
---
<ExperienceLayout title="CEOAS - Robotic Oceanographic Surface Sampler">
<ExperienceLayout
title="Robotic Oceanographic Surface Sampler"
subTitles={subTitles}
>
<Carousel carouselGroup={headerCarouselGroup} />
<div class="mt-4 flex items-center justify-center">
<LinkButton
@@ -62,7 +68,13 @@ const timeline: timelineEntry[] = [
<Timeline timeline={timeline} />
<H3 text="Key Takeaways" />
<ul class="list-inside list-disc">
<li>One</li>
<li>
<div class="inline-block">
Assembled, fabricated, and debugged both custom and
<PopoverWordDefinition key="COTS" />
hardware and electronics.
</div>
</li>
<li>Two</li>
<li>Three</li>
</ul>

View File

@@ -1,9 +1,9 @@
---
import ExperienceLayout from "@layouts/ExperienceLayout.astro";
import Carousel from "@components/Media/CustomCarousel/CustomCarousel.astro";
import type { carouselGroup } from "@interfaces/image-carousel.ts";
import YtVideo from "@components/Media/YtVideo.astro";
import type { carouselGroup } from "@interfaces/image-carousel.ts";
import type { videoConfig } from "@interfaces/video.ts";
import ExperienceLayout from "@layouts/ExperienceLayout.astro";
const headerCarouselGroup: carouselGroup = {
animation: "slide",

View File

@@ -1,7 +1,7 @@
---
import ExperienceLayout from "@layouts/ExperienceLayout.astro";
import Timeline from "@components/Timeline/Timeline.astro";
import Carousel from "@components/Media/CustomCarousel/CustomCarousel.astro";
import Timeline from "@components/Timeline/Timeline.astro";
import ExperienceLayout from "@layouts/ExperienceLayout.astro";
import spring_2019_interns from "@assets/experience/spacex/avionics-test-engineering-internship/spring-2019-interns.jpg";

View File

@@ -1,7 +1,7 @@
---
import ExperienceLayout from "@layouts/ExperienceLayout.astro";
import Timeline from "@components/Timeline/Timeline.astro";
import Carousel from "@components/Media/CustomCarousel/CustomCarousel.astro";
import Timeline from "@components/Timeline/Timeline.astro";
import ExperienceLayout from "@layouts/ExperienceLayout.astro";
import starlink_headquarters_selfie from "@assets/experience/spacex/hardware-test-engineer-i-ii/starlink-headquarters-selfie.jpg";

View File

@@ -1,6 +1,6 @@
---
import HobbyLayout from "@layouts/HobbyLayout.astro";
import Carousel from "@components/Media/CustomCarousel/CustomCarousel.astro";
import HobbyLayout from "@layouts/HobbyLayout.astro";
import type { carouselGroup } from "@interfaces/image-carousel.ts";

View File

@@ -1,6 +1,6 @@
---
import HobbyLayout from "@layouts/HobbyLayout.astro";
import Carousel from "@components/Media/CustomCarousel/CustomCarousel.astro";
import HobbyLayout from "@layouts/HobbyLayout.astro";
import type { carouselGroup } from "@interfaces/image-carousel.ts";

View File

@@ -1,6 +1,6 @@
---
import HobbyLayout from "@layouts/HobbyLayout.astro";
import Carousel from "@components/Media/CustomCarousel/CustomCarousel.astro";
import HobbyLayout from "@layouts/HobbyLayout.astro";
import type { carouselGroup } from "@interfaces/image-carousel.ts";

View File

@@ -1,11 +1,11 @@
---
import HobbyLayout from "@layouts/HobbyLayout.astro";
import Carousel from "@components/Media/CustomCarousel/CustomCarousel.astro";
import HobbyLayout from "@layouts/HobbyLayout.astro";
import type { carouselGroup } from "@interfaces/image-carousel.ts";
import enclosure_front from "@assets/hobby/homelab/offsite-backup-rack/enclosure-front.jpg";
import enclosure_front_pc_panel_open from "@assets/hobby/homelab/offsite-backup-rack/enclosure-front-pc-panel-open.jpg";
import enclosure_front from "@assets/hobby/homelab/offsite-backup-rack/enclosure-front.jpg";
import enclosure_left from "@assets/hobby/homelab/offsite-backup-rack/enclosure-left.jpg";
import enclosure_rear from "@assets/hobby/homelab/offsite-backup-rack/enclosure-rear.jpg";
import enclosure_right from "@assets/hobby/homelab/offsite-backup-rack/enclosure-right.jpg";

View File

@@ -1,16 +1,16 @@
---
import HobbyLayout from "@layouts/HobbyLayout.astro";
import LinkButton from "@components/LinkButton.astro";
import Carousel from "@components/Media/CustomCarousel/CustomCarousel.astro";
import HobbyLayout from "@layouts/HobbyLayout.astro";
import type { carouselGroup } from "@interfaces/image-carousel.ts";
import bottom_fasteners_installed from "@assets/hobby/motorcycling/custom-accessories/chubby-buttons-2-mount/bottom-fasteners-installed.jpg";
import closed_seam from "@assets/hobby/motorcycling/custom-accessories/chubby-buttons-2-mount/closed-seam.jpg";
import closed_top_buttons_installed from "@assets/hobby/motorcycling/custom-accessories/chubby-buttons-2-mount/closed-top-buttons-installed.jpg";
import inside_top_and_bottom from "@assets/hobby/motorcycling/custom-accessories/chubby-buttons-2-mount/inside-top-and-bottom.jpg";
import inside_top_and_bottom_buttons_installed from "@assets/hobby/motorcycling/custom-accessories/chubby-buttons-2-mount/inside-top-and-bottom-buttons-installed.jpg";
import inside_top_and_bottom_with_buttons from "@assets/hobby/motorcycling/custom-accessories/chubby-buttons-2-mount/inside-top-and-bottom-with-buttons.jpg";
import inside_top_and_bottom from "@assets/hobby/motorcycling/custom-accessories/chubby-buttons-2-mount/inside-top-and-bottom.jpg";
import installed_on_bike_handlebars_reference from "@assets/hobby/motorcycling/custom-accessories/chubby-buttons-2-mount/installed-on-bike-handlebars-reference.jpg";
import installed_on_bike_riders_position from "@assets/hobby/motorcycling/custom-accessories/chubby-buttons-2-mount/installed-on-bike-riders-position.jpg";
import top_and_bottom from "@assets/hobby/motorcycling/custom-accessories/chubby-buttons-2-mount/top-and-bottom.jpg";

View File

@@ -1,14 +1,14 @@
---
import HobbyLayout from "@layouts/HobbyLayout.astro";
import Carousel from "@components/Media/CustomCarousel/CustomCarousel.astro";
import HobbyLayout from "@layouts/HobbyLayout.astro";
import type { carouselGroup } from "@interfaces/image-carousel.ts";
import kz750 from "@assets/hobby/motorcycling/lineup/1979-kawasaki-kz750-senior-photo.jpg";
import ninja600 from "@assets/hobby/motorcycling/lineup/1991-kawasaki-ninja-600r.jpg";
import concours from "@assets/hobby/motorcycling/lineup/1999-kawasaki-concours.jpg";
import concours_offroad from "@assets/hobby/motorcycling/lineup/1999-kawasaki-concours-offroad.jpg";
import concours_trailer from "@assets/hobby/motorcycling/lineup/1999-kawasaki-concours-with-trailer.jpg";
import concours from "@assets/hobby/motorcycling/lineup/1999-kawasaki-concours.jpg";
import drz400 from "@assets/hobby/motorcycling/lineup/2005-suzuki-drz-400.jpg";
import fjr1300 from "@assets/hobby/motorcycling/lineup/2015-fjr-1300-mountaintop.jpg";
import sg400 from "@assets/hobby/motorcycling/lineup/2021-csc-sg400.jpg";

View File

@@ -1,6 +1,6 @@
---
import BaseLayout from "../layouts/BaseLayout.astro";
import Carousel from "@components/Media/CustomCarousel/CustomCarousel.astro";
import BaseLayout from "../layouts/BaseLayout.astro";
import type { carouselGroup } from "@interfaces/image-carousel.ts";

View File

@@ -1,6 +1,6 @@
---
import ResumeLayout from "@layouts/ResumeLayout.astro";
import resume from "@assets/resume/corwin_perren_2019-07-01_hardware_test_engineer.pdf";
import ResumeLayout from "@layouts/ResumeLayout.astro";
---
<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 ResumeLayout from "@layouts/ResumeLayout.astro";
---
<ResumeLayout title="2025-10-27 - Infrastructure Engineer" resume={resume} />

View File

@@ -1,6 +1,5 @@
import { test, expect } from "@playwright/test";
import { getPaths } from "@data/site-layout.ts";
import { expect, test } from "@playwright/test";
for (const pagePath of getPaths()) {
test(`${pagePath}: Navigable`, async ({ page }) => {

View File

@@ -1,7 +1,6 @@
import { getPaths, siteLayout } from "@data/site-layout.ts";
import { expect, test } from "vitest";
import { siteLayout, getPaths } from "@data/site-layout.ts";
export const setDifference = <T>(a: Set<T>, b: Set<T>) =>
new Set([...a].filter((x) => !b.has(x)));

View File

@@ -1,5 +1,5 @@
import path from "path";
import { getViteConfig } from "astro/config";
import path from "path";
export default getViteConfig(
{