Compare commits

..

2 Commits

Author SHA1 Message Date
6952b77980 Merge pull request 'Removed unused build, refactored H1-3 to use slot based setup, added visual and aria page highlighting for navbar links, switched all pages to use custom H1-3, better new page/tab handling for inline links' (#12) from website-content-updates into main
All checks were successful
Build and Test - Production / test (push) Successful in 4m29s
Build and Test - Production / build_and_push (push) Successful in 5m1s
Build and Test - Production / deploy_production (push) Successful in 3s
Reviewed-on: #12
2025-12-05 22:39:08 +00:00
4b5f65bfdd Removed unused build, refactored H1-3 to use slot based setup, added visual and aria page highlighting for navbar links, switched all pages to use custom H1-3, better new page/tab handling for inline links
All checks were successful
Build and Test - Staging / test (pull_request) Successful in 4m29s
Build and Test - Staging / build_and_push (pull_request) Successful in 4m59s
Build and Test - Staging / deploy_staging (pull_request) Successful in 3s
2025-12-05 14:17:12 -08:00
18 changed files with 248 additions and 224 deletions

View File

@@ -1,27 +0,0 @@
name: Playwright Tests
on:
push:
branches: [main, master]
pull_request:
branches: [main, master]
jobs:
test:
timeout-minutes: 60
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: lts/*
- name: Install dependencies
run: npm ci
- name: Install Playwright Browsers
run: npx playwright install --with-deps
- name: Run Playwright tests
run: npx playwright test
- uses: actions/upload-artifact@v4
if: ${{ !cancelled() }}
with:
name: playwright-report
path: playwright-report/
retention-days: 30

View File

@@ -1,5 +0,0 @@
---
---
<h2 class="my-4 font-bold md:text-2xl">{Astro.props.text}</h2>

View File

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

5
src/components/H1.astro Normal file
View File

@@ -0,0 +1,5 @@
---
---
<h1 class="text-xl font-extrabold md:text-3xl"><slot /></h1>

5
src/components/H2.astro Normal file
View File

@@ -0,0 +1,5 @@
---
---
<h2 class="my-4 font-bold md:text-2xl"><slot /></h2>

5
src/components/H3.astro Normal file
View File

@@ -0,0 +1,5 @@
---
---
<h3 class="mt-4 mb-2 font-bold md:text-lg"><slot /></h3>

View File

@@ -1,14 +1,30 @@
--- ---
interface Props { import type { ComponentPropsBase } from "@interfaces/components.ts";
interface Props extends ComponentPropsBase {
href: string; href: string;
target?: string; target?: string;
} }
const { href, target = "_blank" } = Astro.props; const { class: className, href, target } = Astro.props;
let finalTarget: string | undefined = target;
if (target === undefined) {
if (href.startsWith("/")) {
finalTarget = "";
} else {
finalTarget = "_blank";
}
}
--- ---
<> <>
<a class="text-blue-500 hover:text-blue-300" href={href} target={target}> <a
class:list={["text-blue-500", "hover:text-blue-300", className]}
href={href}
target={finalTarget}
>
<slot /> <slot />
</a> </a>
</> </>

View File

@@ -14,6 +14,8 @@ const getHrefPath = (entry: navLink): string => {
: "/" + : "/" +
(paths && paths.length ? [...paths, entry.path].join("/") : entry.path); (paths && paths.length ? [...paths, entry.path].join("/") : entry.path);
}; };
const { pathname } = Astro.url;
--- ---
<ul <ul
@@ -26,14 +28,20 @@ const getHrefPath = (entry: navLink): string => {
(entry.enabled ?? true) && ( (entry.enabled ?? true) && (
<li> <li>
{Array.isArray(entry.children) && entry.children.length ? ( {Array.isArray(entry.children) && entry.children.length ? (
<div> <div
class={
pathname.startsWith(getHrefPath(entry))
? "border-caperren-green border-b-2"
: ""
}
>
<button <button
id={"dropdownNavbarLink" + getNavLinkSuffix(entry)} id={"dropdownNavbarLink" + getNavLinkSuffix(entry)}
data-dropdown-toggle={ data-dropdown-toggle={
"dropdownNavbar" + getNavLinkSuffix(entry) "dropdownNavbar" + getNavLinkSuffix(entry)
} }
data-dropdown-placement="bottom" data-dropdown-placement="bottom"
class="hover:text-caperren-green-light lg:hover:text-caperren-green-light flex w-full items-center justify-between px-3 py-2 lg:border-0 lg:p-0 lg:hover:bg-transparent" class="hover:text-caperren-green-light lg:hover:text-caperren-green-light flex w-full items-center justify-between px-3 py-2 lg:p-0 lg:hover:bg-transparent"
> >
{entry.navText} {entry.navText}
<svg <svg
@@ -63,13 +71,24 @@ const getHrefPath = (entry: navLink): string => {
</div> </div>
</div> </div>
) : ( ) : (
<div
class={
pathname === getHrefPath(entry)
? "border-caperren-green border-b-2"
: ""
}
>
<a <a
href={getHrefPath(entry)} href={getHrefPath(entry)}
target={getHrefPath(entry).startsWith("/") ? "" : "_blank"}
class="hover:text-caperren-green-light ring-caperren-green-dark block bg-transparent px-3 py-2 lg:p-0" class="hover:text-caperren-green-light ring-caperren-green-dark block bg-transparent px-3 py-2 lg:p-0"
aria-current="page" aria-current={
pathname === getHrefPath(entry) ? "page" : undefined
}
> >
{entry.navText} {entry.navText}
</a> </a>
</div>
)} )}
</li> </li>
), ),

View File

@@ -0,0 +1,3 @@
export interface ComponentPropsBase {
class?: string;
}

View File

@@ -2,6 +2,7 @@
import "@styles/global.css"; import "@styles/global.css";
import Footer from "@components/Footer.astro"; import Footer from "@components/Footer.astro";
import H1 from "@components/H1.astro";
import Navbar from "@components/Navbar.astro"; import Navbar from "@components/Navbar.astro";
import { pathToMetadata } from "@data/site-layout.ts"; import { pathToMetadata } from "@data/site-layout.ts";
@@ -45,33 +46,23 @@ const pageEnabled = pathToMetadata(Astro.url.pathname).enabled ?? true;
> >
<Navbar /> <Navbar />
<main class="mx-6 my-6"> <main class="mx-6 my-6">
<div class="mb-2 md:mb-6">
{ {
title && showTitle && pageEnabled && ( title && showTitle && pageEnabled && (
<h1 <H1 bottomMargin={!subTitles}>{title}</H1>
class={
"text-xl font-extrabold md:text-3xl " +
(subTitles ? "" : "md:mb-6")
}
>
{title}
</h1>
) )
} }
{ {
showTitle && showTitle &&
pageEnabled && pageEnabled &&
subTitles?.map((subTitle, index) => ( subTitles?.map((subTitle) => (
<p <p class="text-sm font-bold md:text-xl">{subTitle}</p>
class={
"text-sm font-bold md:text-xl " +
(index == subTitles.length - 1 ? "mb-2 md:mb-6" : "")
}
>
{subTitle}
</p>
)) ))
} }
{pageEnabled && <slot />} </div>
<div>
{pageEnabled ? <slot /> : <H1>Under Construction</H1>}
</div>
</main> </main>
</div> </div>
<Footer /> <Footer />

View File

@@ -1,8 +1,12 @@
--- ---
import BaseLayout from "@layouts/BaseLayout.astro";
import H2 from "@components/H2.astro";
import H3 from "@components/H3.astro";
import InlineLink from "@components/InlineLink.astro";
import Carousel from "@components/Media/CustomCarousel/CustomCarousel.astro"; import Carousel from "@components/Media/CustomCarousel/CustomCarousel.astro";
import Table from "@components/Table.astro"; import Table from "@components/Table.astro";
import Timeline from "@components/Timeline/Timeline.astro"; import Timeline from "@components/Timeline/Timeline.astro";
import BaseLayout from "@layouts/BaseLayout.astro";
import type { carouselGroup } from "@interfaces/image-carousel.ts"; import type { carouselGroup } from "@interfaces/image-carousel.ts";
import type { tableData } from "@interfaces/table.ts"; import type { tableData } from "@interfaces/table.ts";
@@ -104,14 +108,16 @@ const courseTable: tableData = {
<BaseLayout title="Education"> <BaseLayout title="Education">
<Carousel carouselGroup={diplomaCarouselGroup} /> <Carousel carouselGroup={diplomaCarouselGroup} />
<h2 class="my-4 font-bold underline md:text-2xl">Timeline</h2> <H2>Timeline</H2>
<Timeline timeline={timeline} /> <Timeline timeline={timeline} />
<h2 class="my-4 font-bold underline md:text-2xl">Oregon State University</h2> <H2>Oregon State University</H2>
<a <H3>
class="my-4 font-bold text-blue-500 underline hover:text-blue-300 md:text-lg" <InlineLink
class="font-bold md:text-lg"
href="https://github.com/caperren/school_archives/tree/master/OSU%20Coursework" href="https://github.com/caperren/school_archives/tree/master/OSU%20Coursework"
>Coursework Archives</a >Coursework Archives</InlineLink
> >
<h3 class="my-4 font-bold underline md:text-lg">Course Listing</h3> </H3>
<H3>Course Listing</H3>
<Table data={courseTable} /> <Table data={courseTable} />
</BaseLayout> </BaseLayout>

View File

@@ -1,8 +1,8 @@
--- ---
import ExperienceLayout from "@layouts/ExperienceLayout.astro"; import ExperienceLayout from "@layouts/ExperienceLayout.astro";
import H2 from "@components/CustomHtmlWrappers/H2.astro"; import H2 from "@components/H2.astro";
import H3 from "@components/CustomHtmlWrappers/H3.astro"; import H3 from "@components/H3.astro";
import Carousel from "@components/Media/CustomCarousel/CustomCarousel.astro"; import Carousel from "@components/Media/CustomCarousel/CustomCarousel.astro";
import Paragraph from "@components/Paragraph.astro"; import Paragraph from "@components/Paragraph.astro";
import Timeline from "@components/Timeline/Timeline.astro"; import Timeline from "@components/Timeline/Timeline.astro";
@@ -65,17 +65,17 @@ import { subTitles } from "./osu-ceoas-ocean-mixing-group.ts";
<ExperienceLayout title="LeConte Glacier Deployments" subTitles={subTitles}> <ExperienceLayout title="LeConte Glacier Deployments" subTitles={subTitles}>
<Carousel carouselGroup={headerCarouselGroup} /> <Carousel carouselGroup={headerCarouselGroup} />
<H2 text="Summary" /> <H2>Summary</H2>
<H3 text="Timeline" /> <H3>Timeline</H3>
<Timeline timeline={deploymentTimeline} /> <Timeline timeline={deploymentTimeline} />
<H3 text="Location" /> <H3>Location</H3>
<iframe <iframe
class="w-full" class="w-full"
width="600" width="600"
height="450" height="450"
src="https://maps.google.com/maps?q=leconte%20glacier&t=k&z=11&ie=UTF8&iwloc=B&output=embed" src="https://maps.google.com/maps?q=leconte%20glacier&t=k&z=11&ie=UTF8&iwloc=B&output=embed"
></iframe> ></iframe>
<H2 text="Details" /> <H2>Details</H2>
<Paragraphs> <Paragraphs>
<Paragraph> <Paragraph>
As part of my time working on the As part of my time working on the
@@ -122,7 +122,7 @@ import { subTitles } from "./osu-ceoas-ocean-mixing-group.ts";
on for the rest of my life. on for the rest of my life.
</Paragraph> </Paragraph>
</Paragraphs> </Paragraphs>
<H2 text="Videos" /> <H2>Videos</H2>
<div class="grid grid-cols-1 gap-4 md:grid-cols-2"> <div class="grid grid-cols-1 gap-4 md:grid-cols-2">
{ {
videos.map((video) => ( videos.map((video) => (

View File

@@ -1,8 +1,8 @@
--- ---
import ExperienceLayout from "@layouts/ExperienceLayout.astro"; import ExperienceLayout from "@layouts/ExperienceLayout.astro";
import H2 from "@components/CustomHtmlWrappers/H2.astro"; import H2 from "@components/H2.astro";
import H3 from "@components/CustomHtmlWrappers/H3.astro"; import H3 from "@components/H3.astro";
import LinkButton from "@components/LinkButton.astro"; import LinkButton from "@components/LinkButton.astro";
import Carousel from "@components/Media/CustomCarousel/CustomCarousel.astro"; import Carousel from "@components/Media/CustomCarousel/CustomCarousel.astro";
import PdfViewer from "@components/Media/PdfViewer.astro"; import PdfViewer from "@components/Media/PdfViewer.astro";
@@ -63,10 +63,10 @@ const timeline: timelineEntry[] = [
title="Official Scientific Publication" title="Official Scientific Publication"
/> />
</div> </div>
<H2 text="Summary" /> <H2>Summary</H2>
<H3 text="Timeline" /> <H3>Timeline</H3>
<Timeline timeline={timeline} /> <Timeline timeline={timeline} />
<H3 text="Key Takeaways" /> <H3>Key Takeaways</H3>
<ul class="list-inside list-disc"> <ul class="list-inside list-disc">
<li> <li>
<div class="inline-block"> <div class="inline-block">
@@ -103,9 +103,9 @@ const timeline: timelineEntry[] = [
<hr class="text-caperren-green" /> <hr class="text-caperren-green" />
</div> </div>
</div> </div>
<H2 text="Details" /> <H2>Details</H2>
power and voltage logging power and voltage logging
<H2 text="Official Scientific Publication" /> <H2>Official Scientific Publication</H2>
<div class="h-334"> <div class="h-334">
<PdfViewer pdf={publication} /> <PdfViewer pdf={publication} />
</div> </div>

View File

@@ -1,7 +1,13 @@
--- ---
import Carousel from "@components/Media/CustomCarousel/CustomCarousel.astro";
import HobbyLayout from "@layouts/HobbyLayout.astro"; import HobbyLayout from "@layouts/HobbyLayout.astro";
import H2 from "@components/H2.astro";
import H3 from "@components/H3.astro";
import InlineLink from "@components/InlineLink.astro";
import Carousel from "@components/Media/CustomCarousel/CustomCarousel.astro";
import Paragraph from "@components/Paragraph.astro";
import Paragraphs from "@components/Paragraphs.astro";
import type { carouselGroup } from "@interfaces/image-carousel.ts"; import type { carouselGroup } from "@interfaces/image-carousel.ts";
import injection_site from "@assets/hobby/body-mods/rfid-implant/injection-site.jpg"; import injection_site from "@assets/hobby/body-mods/rfid-implant/injection-site.jpg";
@@ -21,42 +27,42 @@ const rfidImplantCarouselGroup: carouselGroup = {
--- ---
<HobbyLayout title="Body Mods"> <HobbyLayout title="Body Mods">
<h2 class="my-4 font-bold underline md:text-2xl">RFID Implant</h2> <H2>RFID Implant</H2>
<Carousel carouselGroup={rfidImplantCarouselGroup} /> <Carousel carouselGroup={rfidImplantCarouselGroup} />
<p class="mt-4"> <H3>Details</H3>
Back when I was in college, a few of my friends and I got this crazy idea to <Paragraphs>
all get RFID implants together. They are essentially the same things you'd <Paragraph>
use to microchip a pet, but with a slightly different firmware 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 configuration, allowing scans with any 125KHz-compatible reader. The
implants came from <a implants came from <InlineLink
class="text-blue-500 hover:text-blue-300" href="https://dangerousthings.com/product/xem/"
href="https://dangerousthings.com/product/xem/">dangerousthings.com</a >dangerousthings.com</InlineLink
>, and we were lucky enough to have a vet-med student as a friend who made >, 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 the installation a quick and painless process! I'm glad that I'm not
of needles, as the 16 gauge injector the kit came with was nothing to scoff afraid of needles, as the 16 gauge injector the kit came with was nothing
at. Since healing, you would never know the implant was there, with the site to scoff at. Since healing, you would never know the implant was there,
leaving no scar or visible indication of its presence. with the site leaving no scar or visible indication of its presence.
</p> </Paragraph>
<p class="mt-4"> <Paragraph>
With that out of the way, our group began work on hardware which would 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 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 emulator for typing passwords with a valid scan, a car off-acc-on ignition
replacement, and a fairly specialized modification to the OSU Robotics replacement, and a fairly specialized modification to the OSU Robotics
Club's doorway scanning system so they would support these on top of the 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 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 usb-keyboard emulator actually came to fruition. The electronics and
firmware were handled by <a primary firmware were handled by <InlineLink href="https://nickmccomb.net"
class="text-blue-500 hover:text-blue-300" >Nick McComb</InlineLink
href="https://nickmccomb.net">Nick McComb</a >, enclosure by <InlineLink href="https://dylanthrush.com"
>, enclosure by >Dylan Thrush</InlineLink
<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 >, 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 want to see an example of the keyboard emulator unlocking a PC, check out
the video on <a the video on <InlineLink
class="text-blue-500 hover:text-blue-300"
href="https://nickmccomb.net/college/printed-circuit-boards/computer-access-module" href="https://nickmccomb.net/college/printed-circuit-boards/computer-access-module"
>Nick's website</a >Nick's website</InlineLink
>! >!
</p> </Paragraph>
</Paragraphs>
</HobbyLayout> </HobbyLayout>

View File

@@ -1,7 +1,10 @@
--- ---
import Carousel from "@components/Media/CustomCarousel/CustomCarousel.astro";
import HobbyLayout from "@layouts/HobbyLayout.astro"; import HobbyLayout from "@layouts/HobbyLayout.astro";
import H2 from "@components/H2.astro";
import H3 from "@components/H3.astro";
import Carousel from "@components/Media/CustomCarousel/CustomCarousel.astro";
import type { carouselGroup } from "@interfaces/image-carousel.ts"; import type { carouselGroup } from "@interfaces/image-carousel.ts";
import kz750 from "@assets/hobby/motorcycling/lineup/1979-kawasaki-kz750-senior-photo.jpg"; import kz750 from "@assets/hobby/motorcycling/lineup/1979-kawasaki-kz750-senior-photo.jpg";
@@ -37,15 +40,15 @@ const kz750CarouselGroup: carouselGroup = {
--- ---
<HobbyLayout title="Motorcycling - Lineup"> <HobbyLayout title="Motorcycling - Lineup">
<h2 class="my-4 font-bold underline md:text-2xl">Current Lineup</h2> <H2>Current Lineup</H2>
<h3 class="my-4 font-bold underline md:text-lg">2015 Yamaha FJR 1300</h3> <H3>2015 Yamaha FJR 1300</H3>
<Carousel carouselGroup={fjrCarouselGroup} /> <Carousel carouselGroup={fjrCarouselGroup} />
<h3 class="my-4 font-bold underline md:text-lg">2021 CSC SG400</h3> <H3>2021 CSC SG400</H3>
<Carousel carouselGroup={cscCarouselGroup} /> <Carousel carouselGroup={cscCarouselGroup} />
<h2 class="my-4 font-bold underline md:text-2xl">Prior Lineup</h2> <H2>Prior Lineup</H2>
<h3 class="my-4 font-bold underline md:text-lg">2005 Suzuki DRZ 400</h3> <H3>2005 Suzuki DRZ 400</H3>
<Carousel carouselGroup={drzCarouselGroup} /> <Carousel carouselGroup={drzCarouselGroup} />
<h3 class="my-4 font-bold underline md:text-lg"> <h3 class="my-4 font-bold underline md:text-lg">
@@ -53,9 +56,9 @@ const kz750CarouselGroup: carouselGroup = {
</h3> </h3>
<Carousel carouselGroup={concoursCarouselGroup} /> <Carousel carouselGroup={concoursCarouselGroup} />
<h3 class="my-4 font-bold underline md:text-lg">1979 Kawasaki KZ750</h3> <H3>1979 Kawasaki KZ750</H3>
<Carousel carouselGroup={kz750CarouselGroup} /> <Carousel carouselGroup={kz750CarouselGroup} />
<h3 class="my-4 font-bold underline md:text-lg">1991 Kawasaki Ninja 600R</h3> <H3>1991 Kawasaki Ninja 600R</H3>
<Carousel carouselGroup={ninjaCarouselGroup} /> <Carousel carouselGroup={ninjaCarouselGroup} />
</HobbyLayout> </HobbyLayout>

View File

@@ -1,6 +1,11 @@
--- ---
import BaseLayout from "@layouts/BaseLayout.astro";
import H2 from "@components/H2.astro";
import InlineLink from "@components/InlineLink.astro";
import Carousel from "@components/Media/CustomCarousel/CustomCarousel.astro"; import Carousel from "@components/Media/CustomCarousel/CustomCarousel.astro";
import BaseLayout from "../layouts/BaseLayout.astro"; import Paragraph from "@components/Paragraph.astro";
import Paragraphs from "@components/Paragraphs.astro";
import type { carouselGroup } from "@interfaces/image-carousel.ts"; import type { carouselGroup } from "@interfaces/image-carousel.ts";
@@ -16,55 +21,52 @@ const headerCarouselGroup: carouselGroup = {
<BaseLayout title="About" showTitle={false}> <BaseLayout title="About" showTitle={false}>
<Carousel carouselGroup={headerCarouselGroup} /> <Carousel carouselGroup={headerCarouselGroup} />
<h2 class="my-4 font-bold underline md:text-2xl">Who Am I</h2> <H2>Who Am I</H2>
<p> <Paragraphs>
My name is Corwin Perren, and I'm a multi-disciplinary engineer with a <a <Paragraph>
class="text-blue-500 hover:text-blue-300" My name is Corwin Perren, and I'm a multi-disciplinary engineer with a <InlineLink
href="/education">degree in computer science</a href="/education">degree in computer science</InlineLink
> from Oregon State University. For as long as I can remember, I've been fascinated > from Oregon State University. For as long as I can remember, I've been fascinated
by how things work, never being shy about taking them apart to learn the gritty by how things work, never being shy about taking them apart to learn the gritty
details. At a young age, I began tinkering, adding lights and fans and doorbells details. At a young age, I began tinkering, adding lights and fans and doorbells
to the pretend cardboard box houses my brother and I would play in. Later, I learned to the pretend cardboard box houses my brother and I would play in. Later, I
to solder, work on vehicles and engines, install and run Linux, manage enterprise learned to solder, work on vehicles and engines, install and run Linux, manage
computing infrastructure, build and repair computers, write scripts, and by the enterprise computing infrastructure, build and repair computers, write scripts,
end of high school set out with a clear goal for my college years. I wanted to and by the end of high school set out with a clear goal for my college years.
learn and teach myself enough to be able to think up almost any project, encompassing I wanted to learn and teach myself enough to be able to think up almost any
all facets of engineering, and be capable of driving it to completion with my project, encompassing all facets of engineering, and be capable of driving it
own skill set. to completion with my own skill set.
</p> </Paragraph>
<p class="mt-4"> <Paragraph>
I think young me would be very pleased by how well I managed to achieve that I think young me would be very pleased by how well I managed to achieve
goal! Through college, I learned electronics and PCB design, embedded and pc that goal! Through college, I learned electronics and PCB design, embedded
programming, basic mechanical design and fabrication, on top of learning how and pc programming, basic mechanical design and fabrication, on top of
to work well with others in a team. I quickly realized that robotics was an learning how to work well with others in a team. I quickly realized that
ideal focus due to its inherent multi-disciplinary nature, and joined the robotics was an ideal focus due to its inherent multi-disciplinary nature,
OSU Robotics Club, which introduced me to people who are still my best and joined the OSU Robotics Club, which introduced me to people who are
friends today. Through student engineering jobs, I had the unique still my best friends today. Through student engineering jobs, I had the
opportunity to work on some incredible projects such as the unique opportunity to work on some incredible projects such as the
<a <InlineLink
class="text-blue-500 hover:text-blue-300"
href="/experience/osu-ceoas-ocean-mixing-group/robotic-oceanographic-surface-sampler" href="/experience/osu-ceoas-ocean-mixing-group/robotic-oceanographic-surface-sampler"
>robotic oceanographic surface sampler</a >robotic oceanographic surface sampler</InlineLink
> and an <a >
class="text-blue-500 hover:text-blue-300" and an <InlineLink
href="/experience/osu-sinnhuber-aquatic-research-laboratory/zebrafish-embryo-pick-and-plate" href="/experience/osu-sinnhuber-aquatic-research-laboratory/zebrafish-embryo-pick-and-plate"
>embryo pick-and-plate machine</a >embryo pick-and-plate machine</InlineLink
>. One my my proudest moments was when our club's mars rover took first >. One my my proudest moments was when our club's mars rover took first
place at the Candian International Rover Challenge in 2018, for which I was place at the Candian International Rover Challenge in 2018, for which I
the <a was the <InlineLink
class="text-blue-500 hover:text-blue-300"
href="/experience/osu-robotics-club/mars-rover-software-team-lead" href="/experience/osu-robotics-club/mars-rover-software-team-lead"
>software lead</a >software lead</InlineLink
>! >!
</p> </Paragraph>
<p class="mt-4"> <Paragraph>
After a short three-month <a After a short three-month <InlineLink
class="text-blue-500 hover:text-blue-300"
href="/experience/spacex/avionics-test-engineering-internship" href="/experience/spacex/avionics-test-engineering-internship"
>internship</a >internship</InlineLink
> at SpaceX in Hawthorne at the end of college, I applied for a <a > at SpaceX in Hawthorne at the end of college, I applied for a <InlineLink
class="text-blue-500 hover:text-blue-300" href="/experience/spacex/hardware-test-engineer-i-ii"
href="/experience/spacex/hardware-test-engineer-i-ii">test engineering</a >test engineering</InlineLink
> position with the company's Starlink team and was hired in mid-2019. For six > 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, years, I developed test system hardware, software, harnesses, mechanical fixtures,
devops infrastructure, websites, and tooling to ensure that Starlink, Falcon, devops infrastructure, websites, and tooling to ensure that Starlink, Falcon,
@@ -72,25 +74,23 @@ const headerCarouselGroup: carouselGroup = {
hardware. Through it all, I got to apply and hone every skill I had developed, hardware. Through it all, I got to apply and hone every skill I had developed,
while learning countless more. Now though, it's on to the next adventure, whatever while learning countless more. Now though, it's on to the next adventure, whatever
that may be! that may be!
</p> </Paragraph>
<p class="mt-4"> <Paragraph>
To learn more about my experiences, hobbies, interests, and skills, feel To learn more about my experiences, hobbies, interests, and skills, feel
free to explore the site! While the short summary above provides some free to explore the site! While the short summary above provides some
insight into who I am, it leaves out plenty! For example, I've been an avid <a insight into who I am, it leaves out plenty! For example, I've been an
class="text-blue-500 hover:text-blue-300" avid <InlineLink href="/hobby/motorcycling/lineup"
href="/hobby/motorcycling/lineup">motorcycle rider</a >motorcycle rider</InlineLink
> since I was sixteen, and have an <a > since I was sixteen, and have an <InlineLink href="/hobby/body-mods"
class="text-blue-500 hover:text-blue-300" >rfid implant</InlineLink
href="/hobby/body-mods">rfid implant</a
> in my hand! > in my hand!
</p> </Paragraph>
<p class="mt-4"> <Paragraph>
If you're interested in contacting me, feel free to message on <a If you're interested in contacting me, feel free to message on <InlineLink
class="text-blue-500 hover:text-blue-300" href="https://github.com/caperren">LinkedIn</InlineLink
href="https://github.com/caperren">LinkedIn</a >, or via the primary contact methods on my <InlineLink
>, or via the primary contact methods on my <a href="/resume/2025-11-10-infrastructure-engineer">resume</InlineLink
class="text-blue-500 hover:text-blue-300"
href="/resume/2025-11-10-infrastructure-engineer">resume</a
>. >.
</p> </Paragraph>
</Paragraphs>
</BaseLayout> </BaseLayout>

View File

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

View File

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