Compare commits

..

37 Commits

Author SHA1 Message Date
43d0724345 Merge pull request 'website-content-updates' (#7) from website-content-updates into main
Some checks failed
Build and Test - Production / test (push) Successful in 3m39s
Build and Test - Production / build_and_push (push) Failing after 48s
Build and Test - Production / deploy_production (push) Successful in 3s
Reviewed-on: #7
2025-11-10 09:18:10 +00:00
c966f9302b Added YtVideo, set enables on site layout for first prod deploy
All checks were successful
Build and Test - Staging / test (pull_request) Successful in 3m47s
Build and Test - Staging / build_and_push (pull_request) Successful in 3m21s
Build and Test - Staging / deploy_staging (pull_request) Successful in 5s
2025-11-10 01:08:36 -08:00
47e10ba05f Enabled astro preload, added video components, videos for software lead, part of content for software lead
Some checks failed
Build and Test - Staging / test (pull_request) Failing after 2m10s
Build and Test - Staging / build_and_push (pull_request) Has been skipped
Build and Test - Staging / deploy_staging (pull_request) Has been skipped
2025-11-10 01:06:31 -08:00
199074f10b Chubby buttons mount content
All checks were successful
Build and Test - Staging / test (pull_request) Successful in 3m35s
Build and Test - Staging / build_and_push (pull_request) Successful in 3m11s
Build and Test - Staging / deploy_staging (pull_request) Successful in 4s
2025-11-09 22:59:02 -08:00
4741b36003 Motorcycle lineup content
All checks were successful
Build and Test - Staging / test (pull_request) Successful in 3m42s
Build and Test - Staging / build_and_push (pull_request) Successful in 3m20s
Build and Test - Staging / deploy_staging (pull_request) Successful in 4s
2025-11-09 22:14:43 -08:00
c3673b76b6 Added sitemap, with filtering of disabled pages, removal of content from disabled pages, robots.txt generation pointing to the sitemap, adjustment of the Navbar generation to ignore disabled entries, e2e check for all pages including a non-blank title, content for body mods page
All checks were successful
Build and Test - Staging / test (pull_request) Successful in 2m44s
Build and Test - Staging / build_and_push (pull_request) Successful in 2m14s
Build and Test - Staging / deploy_staging (pull_request) Successful in 6s
2025-11-09 21:50:49 -08:00
d0f5838cac Copy staging test updates to prod
All checks were successful
Build and Test - Staging / test (pull_request) Successful in 3m14s
Build and Test - Staging / build_and_push (pull_request) Successful in 2m26s
Build and Test - Staging / deploy_staging (pull_request) Successful in 5s
2025-11-09 15:56:36 -08:00
5c0152d234 Actually build project before testing
All checks were successful
Build and Test - Staging / test (pull_request) Successful in 2m43s
Build and Test - Staging / build_and_push (pull_request) Successful in 2m35s
Build and Test - Staging / deploy_staging (pull_request) Successful in 5s
2025-11-09 15:52:49 -08:00
c6796e782a Install project deps
Some checks failed
Build and Test - Staging / test (pull_request) Failing after 2m3s
Build and Test - Staging / build_and_push (pull_request) Has been skipped
Build and Test - Staging / deploy_staging (pull_request) Has been skipped
2025-11-09 15:49:39 -08:00
2d7f2904a8 Add in the loop test for navigable pages, plus test workflow
Some checks failed
Build and Test - Staging / test (pull_request) Failing after 27s
Build and Test - Staging / build_and_push (pull_request) Has been skipped
Build and Test - Staging / deploy_staging (pull_request) Has been skipped
2025-11-09 15:45:15 -08:00
6bb862e6e5 Disable custom changes to entrypoint and nginx.conf
All checks were successful
Build and Test - Staging / determine_version (pull_request) Successful in 41s
Build and Test - Staging / build_and_push (pull_request) Successful in 2m42s
Build and Test - Staging / test (pull_request) Successful in 2s
Build and Test - Staging / deploy_staging (pull_request) Successful in 5s
2025-11-09 14:11:02 -08:00
083d52d3a5 Disabling caching for testing
All checks were successful
Build and Test - Staging / determine_version (pull_request) Successful in 32s
Build and Test - Staging / build_and_push (pull_request) Successful in 2m24s
Build and Test - Staging / test (pull_request) Successful in 3s
Build and Test - Staging / deploy_staging (pull_request) Successful in 4s
2025-11-09 14:01:20 -08:00
69c811854c Change link
All checks were successful
Build and Test - Staging / determine_version (pull_request) Successful in 27s
Build and Test - Staging / build_and_push (pull_request) Successful in 2m34s
Build and Test - Staging / test (pull_request) Successful in 2s
Build and Test - Staging / deploy_staging (pull_request) Successful in 5s
2025-11-08 11:44:52 -08:00
adcd336a16 Slow but working is better than not working
All checks were successful
Build and Test - Staging / determine_version (pull_request) Successful in 26s
Build and Test - Staging / build_and_push (pull_request) Successful in 2m27s
Build and Test - Staging / test (pull_request) Successful in 2s
Build and Test - Staging / deploy_staging (pull_request) Successful in 8s
2025-11-08 11:36:26 -08:00
4cd7e565eb Trying a couple more flags
All checks were successful
Build and Test - Staging / determine_version (pull_request) Successful in 32s
Build and Test - Staging / build_and_push (pull_request) Successful in 2m35s
Build and Test - Staging / test (pull_request) Successful in 2s
Build and Test - Staging / deploy_staging (pull_request) Successful in 6s
2025-11-08 01:26:57 -08:00
cf3b740907 Remove caching changes for now, put script back
All checks were successful
Build and Test - Staging / determine_version (pull_request) Successful in 26s
Build and Test - Staging / build_and_push (pull_request) Successful in 2m30s
Build and Test - Staging / test (pull_request) Successful in 2s
Build and Test - Staging / deploy_staging (pull_request) Successful in 6s
2025-11-08 01:06:15 -08:00
297a56e7d6 Try making script load earlier
All checks were successful
Build and Test - Staging / determine_version (pull_request) Successful in 26s
Build and Test - Staging / test (pull_request) Successful in 2s
Build and Test - Staging / build_and_push (pull_request) Successful in 2m22s
Build and Test - Staging / deploy_staging (pull_request) Successful in 5s
2025-11-08 00:59:53 -08:00
ee76972667 Titles for all pages, custom entrypoint with cache invalidator etag setup so site doesn't do bad things on redeploy
All checks were successful
Build and Test - Staging / determine_version (pull_request) Successful in 35s
Build and Test - Staging / build_and_push (pull_request) Successful in 2m26s
Build and Test - Staging / test (pull_request) Successful in 3s
Build and Test - Staging / deploy_staging (pull_request) Successful in 6s
2025-11-08 00:52:32 -08:00
005e092344 Fixed logo title and favicon
All checks were successful
Build and Test - Staging / determine_version (pull_request) Successful in 8m0s
Build and Test - Staging / build_and_push (pull_request) Successful in 14m22s
Build and Test - Staging / test (pull_request) Successful in 1s
Build and Test - Staging / deploy_staging (pull_request) Successful in 2s
2025-11-08 00:20:04 -08:00
ffbc3beaf7 About me page complete, tweak to default size of carousel on large screens, extra photo for homepage.
Some checks failed
Build and Test - Staging / determine_version (pull_request) Successful in 8m25s
Build and Test - Staging / test (pull_request) Has been cancelled
Build and Test - Staging / deploy_staging (pull_request) Has been cancelled
Build and Test - Staging / build_and_push (pull_request) Has been cancelled
2025-11-08 00:06:41 -08:00
506987c164 Trying nginx instead
All checks were successful
Build and Test - Staging / test (pull_request) Successful in 1s
Build and Test - Staging / deploy_staging (pull_request) Successful in 3s
Build and Test - Staging / determine_version (pull_request) Successful in 27s
Build and Test - Staging / build_and_push (pull_request) Successful in 2m35s
2025-11-07 22:26:38 -08:00
a807a39b09 Remove project version, set fixed httpd version
All checks were successful
Build and Test - Staging / build_and_push (pull_request) Successful in 2m24s
Build and Test - Staging / test (pull_request) Successful in 2s
Build and Test - Staging / deploy_staging (pull_request) Successful in 5s
Build and Test - Staging / determine_version (pull_request) Successful in 6m20s
2025-11-07 21:59:49 -08:00
40a673a418 Compartmentalized Timeline and Carousel, applied www-data permissions to all files in built docker container, for deploys to work
All checks were successful
Build and Test - Staging / determine_version (pull_request) Successful in 30s
Build and Test - Staging / build_and_push (pull_request) Successful in 2m21s
Build and Test - Staging / test (pull_request) Successful in 2s
Build and Test - Staging / deploy_staging (pull_request) Successful in 5s
2025-11-07 21:36:08 -08:00
9a4ecdd073 Move inline typescript to dedicated scripts directory
All checks were successful
Build and Test - Staging / determine_version (pull_request) Successful in 24s
Build and Test - Staging / build_and_push (pull_request) Successful in 2m5s
Build and Test - Staging / test (pull_request) Successful in 3s
Build and Test - Staging / deploy_staging (pull_request) Successful in 5s
2025-11-07 20:58:03 -08:00
d24a3054c1 Move script import to end of body
All checks were successful
Build and Test - Staging / determine_version (pull_request) Successful in 26s
Build and Test - Staging / build_and_push (pull_request) Successful in 2m4s
Build and Test - Staging / test (pull_request) Successful in 2s
Build and Test - Staging / deploy_staging (pull_request) Successful in 4s
2025-11-07 20:26:18 -08:00
d1bc55e556 Comment vitest config
All checks were successful
Build and Test - Staging / build_and_push (pull_request) Successful in 2m7s
Build and Test - Staging / test (pull_request) Successful in 3s
Build and Test - Staging / deploy_staging (pull_request) Successful in 6s
Build and Test - Staging / determine_version (pull_request) Successful in 27s
2025-11-07 20:11:30 -08:00
f0319c4446 Put tailwindcss vite back into regular deps
All checks were successful
Build and Test - Staging / build_and_push (pull_request) Successful in 2m0s
Build and Test - Staging / test (pull_request) Successful in 3s
Build and Test - Staging / determine_version (pull_request) Successful in 24s
Build and Test - Staging / deploy_staging (pull_request) Successful in 3s
2025-11-07 20:03:25 -08:00
221587eff9 Disable playwright
All checks were successful
Build and Test - Staging / determine_version (pull_request) Successful in 27s
Build and Test - Staging / build_and_push (pull_request) Successful in 2m8s
Build and Test - Staging / deploy_staging (pull_request) Successful in 4s
Build and Test - Staging / test (pull_request) Successful in 2s
2025-11-07 19:54:04 -08:00
9ac3295127 Disable playwright
Some checks failed
Build and Test - Staging / determine_version (pull_request) Has been cancelled
Build and Test - Staging / build_and_push (pull_request) Has been cancelled
Build and Test - Staging / test (pull_request) Has been cancelled
Build and Test - Staging / deploy_staging (pull_request) Has been cancelled
2025-11-07 19:52:22 -08:00
c5dde92023 Made event handlers attach after page load
Some checks failed
Build and Test - Staging / determine_version (pull_request) Successful in 29s
Playwright Tests / test (pull_request) Has been cancelled
Build and Test - Staging / test (pull_request) Has been cancelled
Build and Test - Staging / deploy_staging (pull_request) Has been cancelled
Build and Test - Staging / build_and_push (pull_request) Has been cancelled
2025-11-07 19:50:46 -08:00
f91be707d9 Upgraded astro and npm packages
Some checks failed
Build and Test - Staging / determine_version (pull_request) Successful in 8m56s
Playwright Tests / test (pull_request) Failing after 9m22s
Build and Test - Staging / build_and_push (pull_request) Successful in 16m18s
Build and Test - Staging / test (pull_request) Successful in 1s
Build and Test - Staging / deploy_staging (pull_request) Successful in 3s
2025-11-07 00:04:04 -08:00
7fe9303d5e No typing for script import
Some checks failed
Build and Test - Staging / determine_version (pull_request) Successful in 27s
Playwright Tests / test (pull_request) Failing after 2m21s
Build and Test - Staging / build_and_push (pull_request) Successful in 2m13s
Build and Test - Staging / test (pull_request) Successful in 2s
Build and Test - Staging / deploy_staging (pull_request) Successful in 6s
2025-11-06 23:40:38 -08:00
33388f59ee Added media for kube, home server rack, body mods, chubby buttons, and home page
Some checks failed
Build and Test - Staging / determine_version (pull_request) Successful in 1m47s
Playwright Tests / test (pull_request) Failing after 2m49s
Build and Test - Staging / build_and_push (pull_request) Successful in 4m36s
Build and Test - Staging / test (pull_request) Successful in 1s
Build and Test - Staging / deploy_staging (pull_request) Successful in 7s
2025-11-06 23:14:19 -08:00
3e34b94ec5 Dark footer, added photos for offsite backup rack 2025-11-06 17:37:33 -08:00
128dc14459 Education page finished, improvements to carousel, placeholder content from old website 2025-11-06 16:47:10 -08:00
d6e75ae2ea Made a baseline working carousel, timeline, and started flushing out content for primary spacex experience 2025-11-06 01:21:27 -08:00
6f728ad146 Responsive navigation fully working, placeholders for all major items on site, resume pages working, abstracted site-layout, preparing for unit and e2e testing, added flotbite + tailwind + vitest + playwright 2025-11-05 03:13:05 -08:00
134 changed files with 3668 additions and 1441 deletions

View File

@@ -0,0 +1,27 @@
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

@@ -4,7 +4,7 @@ on:
branches: [main]
jobs:
determine_version:
test:
runs-on: ubuntu-latest
outputs:
repo_name: ${{ steps.project_metadata.outputs.REPO_NAME }}
@@ -17,16 +17,29 @@ jobs:
- name: Setup Node Environment
uses: actions/setup-node@v4
- name: Acquire Project Metadata
- name: Setup Project Dependencies
run: |
npm ci
npx playwright install --with-deps
- name: Build Project
run: npm run build
- name: Run Unit Tests
run: npm run test
- name: Run E2E Tests
run: npm run e2e-test
- name: Set Project Metadata
id: project_metadata
run: |
echo REPO_NAME=$(echo ${GITHUB_REPOSITORY} | awk -F"/" '{print $2}') >> $GITHUB_OUTPUT
echo REPO_VERSION_HASH=$(git describe --tags --always | sed 's/^v//') >> $GITHUB_OUTPUT
echo PROJECT_VERSION=$(npm pkg get version --workspaces=false | tr -d \") >> $GITHUB_OUTPUT
build_and_push:
runs-on: ubuntu-latest
needs: determine_version
needs: test
steps:
- name: Checkout caperren-com Repository
uses: actions/checkout@v4
@@ -54,15 +67,8 @@ jobs:
gitea.perren.cloud/caperren/caperren-com:latest
build-args: |
REPO_VERSION_HASH=${{ needs.determine_version.outputs.repo_version_hash }}
PROJECT_VERSION=${{ needs.determine_version.outputs.project_version }}
BUILD_ENVIRONMENT=production
test:
runs-on: ubuntu-latest
needs: build_and_push
steps:
- run: echo "Placeholder"
deploy_production:
runs-on: ubuntu-latest
needs: test

View File

@@ -4,7 +4,7 @@ on:
types: [ opened, synchronize, reopened ]
jobs:
determine_version:
test:
runs-on: ubuntu-latest
outputs:
repo_name: ${{ steps.project_metadata.outputs.REPO_NAME }}
@@ -17,16 +17,29 @@ jobs:
- name: Setup Node Environment
uses: actions/setup-node@v4
- name: Acquire Project Metadata
- name: Setup Project Dependencies
run: |
npm ci
npx playwright install --with-deps
- name: Build Project
run: npm run build
- name: Run Unit Tests
run: npm run test
- name: Run E2E Tests
run: npm run e2e-test
- name: Set Project Metadata
id: project_metadata
run: |
echo REPO_NAME=$(echo ${GITHUB_REPOSITORY} | awk -F"/" '{print $2}') >> $GITHUB_OUTPUT
echo REPO_VERSION_HASH=$(git describe --tags --always | sed 's/^v//') >> $GITHUB_OUTPUT
echo PROJECT_VERSION=$(npm pkg get version --workspaces=false | tr -d \") >> $GITHUB_OUTPUT
build_and_push:
runs-on: ubuntu-latest
needs: determine_version
needs: test
steps:
- name: Checkout caperren-com Repository
uses: actions/checkout@v4
@@ -52,15 +65,8 @@ jobs:
gitea.perren.cloud/caperren/caperren-com:latest-staging
build-args: |
REPO_VERSION_HASH=${{ needs.determine_version.outputs.repo_version_hash }}
PROJECT_VERSION=${{ needs.determine_version.outputs.project_version }}
BUILD_ENVIRONMENT=staging
test:
runs-on: ubuntu-latest
needs: build_and_push
steps:
- run: echo "Placeholder"
deploy_staging:
runs-on: ubuntu-latest
needs: test

7
.gitignore vendored
View File

@@ -28,3 +28,10 @@ pnpm-debug.log*
# jetbrains setting folder
.idea/
# Playwright
/test-results/
/playwright-report/
/blob-report/
/playwright/.cache/
/playwright/.auth/

View File

@@ -8,27 +8,26 @@ COPY package.json package-lock.json tsconfig.json astro.config.mjs ./
FROM base AS prod-deps
RUN npm install --omit=dev
FROM base AS build-deps
RUN npm install
FROM build-deps AS build
FROM prod-deps AS build
COPY . .
ARG REPO_VERSION_HASH
ARG BUILD_ENVIRONMENT
ARG PROJECT_VERSION
RUN echo "PUBLIC_REPO_VERSION_HASH=\"${REPO_VERSION_HASH}\" \n\
PUBLIC_BUILD_ENVIRONMENT=\"${BUILD_ENVIRONMENT}\" \n\
PUBLIC_PROJECT_VERSION=\"${PROJECT_VERSION}\"" >> .env
PUBLIC_BUILD_ENVIRONMENT=\"${BUILD_ENVIRONMENT}\"" >> .env
RUN npm run build
FROM httpd:latest AS runtime
WORKDIR /usr/local/apache2/htdocs
FROM nginx:alpine AS runtime
RUN rm index.html
COPY ./nginx/nginx.conf /etc/nginx/nginx.conf
COPY --from=build /app/dist /usr/share/nginx/html
COPY --from=build /app/dist .
RUN chown -R nginx:nginx /usr/share/nginx/html
#COPY entrypoint.sh /entrypoint.sh
EXPOSE 80
#ENTRYPOINT ["/entrypoint.sh"]

View File

@@ -5,7 +5,8 @@
fix \
astro_upgrade \
build \
dev
dev \
dev-hosted
default: dev
@@ -24,3 +25,6 @@ build:
dev:
npm run dev
dev-hosted:
npm run dev-hosted

View File

@@ -1,18 +1,25 @@
// @ts-check
import { defineConfig } from 'astro/config';
import rehypeAstroRelativeMarkdownLinks from "astro-rehype-relative-markdown-links";
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: [],
markdown: {
rehypePlugins: [rehypeAstroRelativeMarkdownLinks],
},
vite: {
plugins: [tailwindcss()],
},
site: "https://caperren.com",
prefetch: {
prefetchAll: true
},
integrations: [
sitemap({
filter: (pagePath) =>
!disabledPaths.some(disabledPath => pagePath.includes(disabledPath))
})
],
vite: {
plugins: [tailwindcss()],
},
});

14
entrypoint.sh Executable file
View File

@@ -0,0 +1,14 @@
#!/usr/bin/env sh
set -e
# Generate a unique token per container launch
TOKEN="$(cat /proc/sys/kernel/random/uuid)" # or: TOKEN="$(date +%s%N)"
# Write it into a file NGINX will include
cat >/etc/nginx/conf.d/_release_token.conf <<EOF
# auto-generated at container start
set \$release_token "$TOKEN";
EOF
# Then exec nginx
exec nginx -g 'daemon off;'

41
nginx/nginx.conf Normal file
View File

@@ -0,0 +1,41 @@
worker_processes 4;
events {
worker_connections 1024;
}
http {
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my_cache:10m max_size=1g inactive=60m use_temp_path=off;
server {
listen 80;
server_name _;
root /usr/share/nginx/html;
index index.html index.htm;
include /etc/nginx/mime.types;
#include /etc/nginx/conf.d/_release_token.conf;
#etag off;
#add_header ETag "\"W/$release_token\"" always;
#add_header Cache-Control "max-age=0, must-revalidate" always;
gzip on;
gzip_proxied any;
gzip_types text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript;
error_page 404 /404.html;
location = /404.html {
root /usr/share/nginx/html;
internal;
}
location / {
proxy_cache my_cache;
proxy_cache_valid 200 1h;
try_files $uri $uri/index.html =404;
}
}
}

2744
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -4,15 +4,27 @@
"version": "0.0.1",
"scripts": {
"dev": "astro dev",
"dev-hosted": "astro dev --host",
"build": "astro build",
"preview": "astro preview",
"astro": "astro"
"astro": "astro",
"test": "vitest",
"e2e-test": "playwright test"
},
"dependencies": {
"@astrojs/sitemap": "^3.6.0",
"@tailwindcss/vite": "^4.1.11",
"astro": "^5.11.0",
"astro-navbar": "^2.4.0",
"astro-rehype-relative-markdown-links": "^0.18.1",
"tailwindcss": "^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",
"uuid": "^13.0.0"
},
"devDependencies": {
"@playwright/test": "^1.56.1",
"@types/luxon": "^3.7.1",
"@types/node": "^24.10.0",
"vitest": "^4.0.7"
}
}

80
playwright.config.ts Normal file
View File

@@ -0,0 +1,80 @@
import {defineConfig, devices} from '@playwright/test';
/**
* Read environment variables from file.
* https://github.com/motdotla/dotenv
*/
// import dotenv from 'dotenv';
// import path from 'path';
// dotenv.config({ path: path.resolve(__dirname, '.env') });
/**
* See https://playwright.dev/docs/test-configuration.
*/
export default defineConfig({
testDir: './test-e2e',
/* Run tests in files in parallel */
fullyParallel: true,
/* Fail the build on CI if you accidentally left test.only in the source code. */
forbidOnly: !!process.env.CI,
/* Retry on CI only */
retries: process.env.CI ? 2 : 0,
/* Opt out of parallel tests on CI. */
workers: process.env.CI ? 1 : undefined,
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
reporter: 'html',
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
use: {
/* Base URL to use in actions like `await page.goto('')`. */
baseURL: 'http://localhost:4321',
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: 'on-first-retry',
},
/* Configure projects for major browsers */
projects: [
{
name: 'chromium',
use: {...devices['Desktop Chrome']},
},
// {
// name: 'firefox',
// use: { ...devices['Desktop Firefox'] },
// },
//
// {
// name: 'webkit',
// use: { ...devices['Desktop Safari'] },
// },
/* Test against mobile viewports. */
{
name: 'Mobile Chrome',
use: {...devices['Pixel 5']},
},
// {
// name: 'Mobile Safari',
// use: { ...devices['iPhone 12'] },
// },
/* Test against branded browsers. */
// {
// name: 'Microsoft Edge',
// use: { ...devices['Desktop Edge'], channel: 'msedge' },
// },
// {
// name: 'Google Chrome',
// use: { ...devices['Desktop Chrome'], channel: 'chrome' },
// },
],
/* Run your local dev server before starting the tests */
webServer: {
command: 'npm run preview',
url: 'http://localhost:4321',
timeout: 120 * 1000,
reuseExistingServer: !process.env.CI,
},
});

BIN
public/favicon-solid.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 662 B

View File

@@ -1,63 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="40mm"
height="40mm"
viewBox="0 0 40 40"
version="1.1"
id="svg1"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1" />
<g
id="layer1">
<rect
style="fill:#000000;stroke-width:0.264583"
id="rect1"
width="58.244999"
height="53.954998"
x="-6.105"
y="-4.2899995" />
</g>
<g
id="layer2">
<g
id="g1"
transform="matrix(3.9840159,0,0,3.9840159,-182.95899,-268.96729)">
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:3.38667px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#10ac25;fill-opacity:1;stroke:none;stroke-width:0.282222px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="46.9911"
y="76.640976"
id="text3344-4-5"><tspan
id="tspan3346-7-6"
x="46.9911"
y="76.640976"
style="font-size:11.2889px;line-height:1.25;stroke-width:0.282222px">C</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:3.38667px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#10ac25;fill-opacity:1;stroke:none;stroke-width:0.282222px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="49.597134"
y="74.229248"
id="text3348-1-8"
transform="scale(1.0028223,0.99718564)"><tspan
id="tspan3350-3-1"
x="49.597134"
y="74.229248"
style="font-size:4.59543px;line-height:1.25;stroke-width:0.282222px">A</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.38667px;line-height:0%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#10ac25;fill-opacity:1;stroke:none;stroke-width:0.282222px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="24.869509"
y="83.989883"
id="text3344-5-7-6"
transform="matrix(0.89528488,-0.34221418,0.35348636,0.98184609,0,0)"><tspan
id="tspan3370-7-8"
x="24.869509"
y="83.989883"
style="font-size:4.18595px;line-height:1.25;stroke-width:0.282222px">P</tspan></text>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 MiB

View File

Before

Width:  |  Height:  |  Size: 956 KiB

After

Width:  |  Height:  |  Size: 956 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 288 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 721 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 406 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 344 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 382 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 317 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 922 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 186 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 731 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

View File

@@ -2,13 +2,14 @@
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
class="logo-title"
width="21.129419mm"
height="9.3295746mm"
viewBox="0 0 74.868021 33.057548"
id="svg4376"
version="1.1"
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
sodipodi:docname="AP in C Green Mock UP.svg"
sodipodi:docname="logo-title.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

View File

@@ -1,8 +0,0 @@
---
const { images } = Astro.props;
---
<div class="carousel">
{images.map(img => (
<img src={img} alt="carousel item" style="width:100%; max-width:600px; margin: 1rem auto; display:block;" />
))}
</div>

View File

@@ -0,0 +1,96 @@
---
import {Image} from 'astro:assets';
import type {carouselGroup} from "@interfaces/image-carousel.ts";
const groupToShow: carouselGroup = Astro.props.carouselGroup;
const limitByHeightClasses = "sm:max-w-xl md:max-w-3xl lg:max-w-5xl xl:max-w-7xl";
const limitByWidthClasses = "max-h-fit";
---
<custom-carousel class="flex flex-col relative w-full"
data-custom-carousel={groupToShow.animation}
data-custom-carousel-interval={groupToShow.interval}>
<!-- Carousel wrapper -->
<div class="relative overflow-hidden w-full h-56 md:h-120">
{
groupToShow.images.map((image, index) => (
<div class="hidden duration-700 ease-in-out" data-custom-carousel-item={index}>
<Image src={image}
class="absolute inset-0 m-auto h-full w-auto max-h-full max-w-full object-contain"
alt="..."
layout='constrained'
loading="eager"/>
</div>
))
}
</div>
{(groupToShow.images.length > 1) && (
<!-- Slider indicators -->
<div class="absolute z-30 flex -translate-x-1/2 bottom-2 left-1/2 space-x-3 rounded-full">
{
groupToShow.images.map((_, index) => (
<button type="button"
class="w-3 h-3 rounded-full bg-black hover:bg-caperren-green-light"
aria-current={index ? "false" : "true"}
aria-label={index.toString()}
data-custom-carousel-slide-to={index}></button>
))
}
</div>
<!-- Slider controls -->
<button
type="button"
class="absolute top-0 start-0 z-30 flex items-center justify-center h-full px-4 cursor-pointer group focus:outline-none"
data-custom-carousel-prev
>
<span class="inline-flex items-center justify-center w-10 h-10 rounded-full ring-2 ring-caperren-green/25 bg-black/25 group-hover:bg-black/75">
<svg
class="h-4 w-4 text-caperren-green group-hover:text-caperren-green-light"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 6 10"
>
<path
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M5 1 1 5l4 4"
/>
</svg>
<span class="hidden">Previous</span>
</span>
</button>
<button
type="button"
class="absolute top-0 end-0 z-30 flex items-center justify-center h-full px-4 cursor-pointer group focus:outline-none"
data-custom-carousel-next
>
<span class="inline-flex items-center justify-center w-10 h-10 rounded-full ring-2 ring-caperren-green/25 bg-black/25 group-hover:bg-black/75">
<svg
class="h-4 w-4 text-caperren-green group-hover:text-caperren-green-light "
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 6 10"
>
<path
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="m1 9 4-4-4-4"
/>
</svg>
<span class="hidden">Next</span>
</span>
</button>
)}
</custom-carousel>
<script src="./custom-carousel.ts"/>

View File

@@ -0,0 +1,80 @@
import {Carousel, type CarouselItem, type CarouselOptions, type IndicatorItem} from 'flowbite';
class CustomCarousel extends HTMLElement {
_slide: boolean;
_items: CarouselItem[];
_options: CarouselOptions;
_carousel: Carousel;
constructor() {
super();
this._slide = this.getAttribute('data-custom-carousel') === 'slide';
this._items = this._getItems();
this._options = this._getOptions();
this._carousel = new Carousel(this, this._items, this._options);
if (this._slide && this._items.length > 0) this._carousel.cycle();
window.addEventListener("load", this._attachHandlers);
}
_getItems = (): CarouselItem[] => {
let customItems = this.querySelectorAll('[data-custom-carousel-item]') || [];
return Array.from(customItems).map(
(item, index): CarouselItem => {
return {el: item as HTMLElement, position: index}
}
)
}
_getOptions = (): CarouselOptions => {
let customIndicators = this.querySelectorAll('[data-custom-carousel-slide-to]') || [];
return {
defaultPosition: 0,
interval: this.dataset.customCarouselInterval ? Number(this.dataset.customCarouselInterval) : 8000,
indicators: {
activeClasses: 'border-2 border-caperren-green bg-black',
inactiveClasses: 'bg-caperren-green/40 hover:bg-caperren-green-light',
items: Array.from(customIndicators).map(
(item, index): IndicatorItem => {
return {
el: item as HTMLElement,
position: Number(item.getAttribute('data-custom-carousel-slide-to'))
}
}
)
}
}
}
_attachHandlers = (): void => {
// Controls
const carouselNextEl = this.querySelector(
'[data-custom-carousel-next]'
);
const carouselPrevEl = this.querySelector(
'[data-custom-carousel-prev]'
);
if (carouselNextEl) {
carouselNextEl.addEventListener('click', () => {
this._carousel.next();
});
}
if (carouselPrevEl) {
carouselPrevEl.addEventListener('click', () => {
this._carousel.prev();
});
}
}
}
customElements.define('custom-carousel', CustomCarousel)

View File

@@ -1,5 +1,7 @@
---
---
<footer class="flex justify-center items-center text-center">
{import.meta.env.PUBLIC_BUILD_ENVIRONMENT || "development"} | {import.meta.env.PUBLIC_REPO_VERSION_HASH || "invalid"}@{import.meta.env.PUBLIC_PROJECT_VERSION || "0.0.0"}
<footer class="fixed bottom-0 left-0 z-20 w-full px-6 py-2 bg-black border-t border-t-caperren-green-dark text-caperren-green-dark text-sm flex items-center justify-between">
<span>{import.meta.env.PUBLIC_BUILD_ENVIRONMENT || "development"}</span>
<span>{import.meta.env.PUBLIC_REPO_VERSION_HASH || "invalid"}</span>
</footer>

View File

@@ -1,150 +1,34 @@
---
import {Image} from 'astro:assets';
import {Astronav, MenuItems, MenuIcon, Dropdown, DropdownItems} from "astro-navbar";
import NestedNavbarEntry from "@components/NestedNavbarEntries.astro";
import logoTitle from "../assets/logo-title.svg";
const navItems = [
{name: 'Home', href: '/'},
{
name: 'Experience↓',
href: "#",
dropdown: [
{name: 'SpaceX - Hardware Test Engineer II', href: '/experience/spacex-hardware-test-engineer'},
{
name: 'SpaceX - Avionics Test Engineer (Intern)',
href: '/experience/spacex-avionics-test-engineer-intern'
},
{name: 'SARL - Automation Engineer (Student)', href: '/experience/sarl-automation-engineer-student'},
{
name: 'CEOAS OMG - Software/Electronics Engineer (Student)',
href: '/experience/ceoas-omg-software-electronics-engineer-student'
},
{
name: 'OSURC - Member/Officer/Sub-Team Lead (Student)',
href: '/experience/osurc-member-officer-sub-team-lead'
},
]
},
{name: 'Projects↓', href: '/project/project'},
{name: 'Hobbies↓', href: '/hobby/hobby'},
{name: 'Resume', href: '/resume'},
{name: 'Contact', href: '/contact'}
];
import logo_title_large from "@assets/logo-title-large.png";
import {siteLayout} from "@data/site-layout.ts";
import {Image} from "astro:assets";
---
<header class="navbar md:flex p-5 gap-5">
<Astronav>
<div class="flex w-full justify-between">
<a href="/">
<Image src={logoTitle} alt="Logo Title" height="50" loading="eager"/>
</a>
<div class="block md:hidden">
<MenuIcon class="w-4 h-4 text-gray-800"/>
</div>
<nav class="border-b-4 border-b-caperren-green text-caperren-green">
<div class="flex flex-wrap items-center justify-between mx-auto p-6">
<a href="/">
<Image src={logo_title_large}
height="56"
width="139"
class="h-14 w-auto"
alt="logo title"
loading="eager"
/>
</a>
<button data-collapse-toggle="navbar-multi-level" type="button"
class="inline-flex items-center p-2 w-10 h-10 justify-center text-sm md:hidden focus:ring-2 focus:ring-caperren-green"
aria-controls="navbar-multi-level" aria-expanded="false">
<span class="sr-only">Open main menu</span>
<svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 17 14">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M1 1h15M1 7h15M1 13h15"/>
</svg>
</button>
<div class="z-40 hidden w-full md:block md:w-auto" id="navbar-multi-level">
<NestedNavbarEntry items={siteLayout}/>
</div>
<MenuItems class="hidden md:flex">
<ul class="flex flex-col md:flex-row md:gap-5">
<li>
<a href="/">Home</a>
</li>
<!--<li>-->
<!-- <Dropdown class="group">-->
<!-- <button class="flex items-center">-->
<!-- <span> Experience </span>-->
<!-- <svg-->
<!-- xmlns="http://www.w3.org/2000/svg"-->
<!-- fill="none"-->
<!-- viewBox="0 0 24 24"-->
<!-- stroke-width="3"-->
<!-- stroke="currentColor"-->
<!-- class="w-3 h-3 mt-0.5 group-open:rotate-180">-->
<!-- <path-->
<!-- stroke-linecap="round"-->
<!-- stroke-linejoin="round"-->
<!-- d="M19.5 8.25l-7.5 7.5-7.5-7.5"></path>-->
<!-- </svg>-->
<!-- </button>-->
<!-- <DropdownItems class="relative">-->
<!-- <div class="bg-black px-1 py-2 absolute top-0">-->
<!-- <ul>-->
<!-- <li>-->
<!-- <a class="whitespace-nowrap" href="#">SpaceX</a>-->
<!-- </li>-->
<!-- <li>-->
<!-- <a class="whitespace-nowrap" href="#">Sinnhuber Aquatic Research Laboratory</a>-->
<!-- </li>-->
<!-- <li>-->
<!-- <a class="whitespace-nowrap" href="#">CEOAS Ocean Mixing Group</a>-->
<!-- </li>-->
<!-- </ul>-->
<!-- </div>-->
<!-- </DropdownItems>-->
<!-- </Dropdown>-->
<!--</li>-->
<!--<li>-->
<!-- <Dropdown class="group">-->
<!-- <button class="flex items-center">-->
<!-- <span> Projects </span>-->
<!-- <svg-->
<!-- xmlns="http://www.w3.org/2000/svg"-->
<!-- fill="none"-->
<!-- viewBox="0 0 24 24"-->
<!-- stroke-width="3"-->
<!-- stroke="currentColor"-->
<!-- class="w-3 h-3 mt-0.5 group-open:rotate-180">-->
<!-- <path-->
<!-- stroke-linecap="round"-->
<!-- stroke-linejoin="round"-->
<!-- d="M19.5 8.25l-7.5 7.5-7.5-7.5"></path>-->
<!-- </svg>-->
<!-- </button>-->
<!-- <DropdownItems class="relative">-->
<!-- <div class="bg-black absolute top-0">-->
<!-- <ul>-->
<!-- <li>-->
<!-- <a href="#">Placeholder 1</a>-->
<!-- </li>-->
<!-- </ul>-->
<!-- </div>-->
<!-- </DropdownItems>-->
<!-- </Dropdown>-->
<!--</li>-->
<!--<li>-->
<!-- <Dropdown class="group">-->
<!-- <button class="flex items-center">-->
<!-- <span> Hobbies </span>-->
<!-- <svg-->
<!-- xmlns="http://www.w3.org/2000/svg"-->
<!-- fill="none"-->
<!-- viewBox="0 0 24 24"-->
<!-- stroke-width="3"-->
<!-- stroke="currentColor"-->
<!-- class="w-3 h-3 mt-0.5 group-open:rotate-180">-->
<!-- <path-->
<!-- stroke-linecap="round"-->
<!-- stroke-linejoin="round"-->
<!-- d="M19.5 8.25l-7.5 7.5-7.5-7.5"></path>-->
<!-- </svg>-->
<!-- </button>-->
<!-- <DropdownItems class="relative">-->
<!-- <div class="bg-black absolute top-0">-->
<!-- <ul>-->
<!-- <li>Menu 1</li>-->
<!-- <li>Menu 2</li>-->
<!-- <li>Menu 3</li>-->
<!-- </ul>-->
<!-- </div>-->
<!-- </DropdownItems>-->
<!-- </Dropdown>-->
<!--</li>-->
<!--<li>-->
<!-- <a href="/resume">Resume</a>-->
<!--</li>-->
<li>
<a href="/contact">Contact</a>
</li>
</ul>
</MenuItems>
</Astronav>
</header>
</div>
</nav>

View File

@@ -0,0 +1,51 @@
---
import type {navLink} from "@interfaces/site-layout.ts";
const items: navLink[] = Astro.props.items;
const depth: number = Astro.props.depth ?? 0;
const paths: string[] = Astro.props.paths ?? [];
const getNavLinkSuffix = (entry: navLink): string => {
return "-" + [...paths, entry.path].join("-")
}
const getHrefPath = (entry: navLink): string => {
return entry.pubpath ? entry.pubpath : ("/" + (paths && paths.length ? [...paths, entry.path].join("/") : entry.path));
}
---
<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) => (
(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"
fill="none" viewBox="0 0 10 6">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
stroke-width="2"
d="m1 1 4 4 4-4"/>
</svg>
</button>
<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}/>
</div>
) : (
<a href={getHrefPath(entry)}
class="block py-2 px-3 bg-transparent hover:text-caperren-green-light ring-caperren-green-dark md:p-0"
aria-current="page">{entry.navText}</a>
)}
</li>
)
))}
</ul>

View File

@@ -0,0 +1,34 @@
---
import type {tableData} from "@interfaces/table.ts";
const data: tableData = Astro.props.data;
const columnPadding: number = data.columnPadding || 2;
const rowPadding: number = data.columnPadding || 2;
const paddingClasses: string = `px-${columnPadding} py-${rowPadding}`;
---
<div class="relative max-w-full overflow-x-auto">
<table class="w-full text-sm text-left">
<thead class="text-xs border-b-3 border-caperren-green uppercase bg-black">
<tr>
{data.header.map(headingText => (
<th scope="col" class={paddingClasses}>
{headingText}
</th>
))}
</tr>
</thead>
<tbody>
{data.rows.map(row => (
<tr class=" border-b dark:bg-black border-caperren-green">
{row.map(rowColumnText => (
<th scope="row"
class={paddingClasses + " font-medium whitespace-nowrap"}>
{rowColumnText}
</th>
))}
</tr>
))}
</tbody>
</table>
</div>

View File

@@ -0,0 +1,34 @@
---
import type {timelineEntry} from "@interfaces/timeline.ts";
const timeline: timelineEntry[] = Astro.props.timeline || [];
---
<custom-timeline>
<div class="relative z-10 grid gap-6 grid-flow-row sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 3xl:grid-cols-6"
data-timeline>
{timeline.map((entry, index) => (
<div class="pt-1 border bg-black border-caperren-green rounded-lg min-w-s max-w-s px-2 pb-2"
data-timeline-node-index={index}
>
<h3 class="text-lg font-bold">
{entry.event}
</h3>
<h4 class="font-semibold leading-none">
{entry.eventDetail}
</h4>
<time class="mb-2 mt-1 text-sm italic leading-none">
{entry.date}
</time>
{entry.description && (
<p class="text-sm font-normal">
{entry.description}
</p>
)}
</div>
))}
</div>
</custom-timeline>
<script src="./timeline.ts"/>

View File

@@ -0,0 +1,44 @@
import LeaderLine from "leader-line-new";
class CustomTimeline extends HTMLElement {
_eventElements: Element[];
constructor() {
super();
this._eventElements = this._getNodeElements();
window.addEventListener("load", this._paintLeaderLines);
}
_getNodeElements = (): Element[] =>
Array.from(this.querySelectorAll('[data-timeline-node-index]')).sort(
(elementA, elementB) =>
Number(elementA.getAttribute('data-timeline-node-index')) - Number(elementB.getAttribute('data-timeline-node-index'))
);
_paintLeaderLines = () => {
let pairs = this._eventElements.map((entry, index) => {
if (index < this._eventElements.length - 1) {
return [entry, this._eventElements[index + 1]];
}
}).filter(pair => pair !== undefined)
pairs.forEach(pair => {
new LeaderLine({
start: pair[0],
end: pair[1],
color: '#10ac25',
size: 3,
startSocket: "right",
endSocket: "left",
startPlug: "square",
endPlug: "arrow3"
});
});
}
}
customElements.define('custom-timeline', CustomTimeline)

View File

@@ -0,0 +1,12 @@
---
import {type videoConfig} from "@interfaces/video.ts";
const config: videoConfig = Astro.props.videoConfig;
console.log(config);
---
<video class="w-full h-auto max-w-1/2" controls>
<source src={config.videoPath} type={config.videoType ?? "video/mp4"}/>
Your browser does not support the video tag.
</video>

View File

@@ -0,0 +1,9 @@
---
import type {videoConfig} from "@interfaces/video.ts";
const config: videoConfig = Astro.props.videoConfig;
---
<iframe class="h-128 w-full max-w-1/2" src={config.videoPath}
title={config.videoTitle ?? ""}
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>

226
src/data/site-layout.ts Normal file
View File

@@ -0,0 +1,226 @@
import type {navLink} from "@interfaces/site-layout.ts"
export const siteLayout: navLink[] = [
{navText: "About", path: ""},
{navText: "Education", path: "education"},
{
enabled: false,
navText: "Experiences",
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"
}
]
},
{
enabled: false,
navText: "OSU CEOAS",
path: "osu-ceoas-ocean-mixing-group",
children: [
{
enabled: false,
navText: "Robotics Oceanographic Surface Sampler",
path: "robotic-oceanographic-surface-sampler",
},
{
enabled: false,
navText: "LeConte Glacier Deployments",
path: "leconte-glacier-deployments",
}
]
},
{
enabled: false,
navText: "OSU SARL",
path: "osu-sinnhuber-aquatic-research-laboratory",
children: [
{
enabled: false,
navText: "Team Lead",
path: "team-lead",
},
{
enabled: false,
navText: "Zebrafish Embryo Pick and Plate",
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",
}
]
},
{
enabled: false,
navText: "OSU Robotics Club",
path: "osu-robotics-club",
children: [
{
enabled: false,
navText: "Mars Rover Software Team Lead",
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",
}
]
},
]
},
{
navText: "Hobbies",
path: "hobby",
children: [
{
enabled: false,
navText: "Homelab", path: "homelab",
children: [
{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"},
]
},
{
navText: "Motorcycling",
path: "motorcycling",
children: [
{navText: "Lineup", path: "lineup"},
{
navText: "Custom Accessories",
path: "custom-accessories",
children: [
{navText: "Chubby Buttons 2 Mount", path: "chubby-buttons-2-mount"},
]
},
{
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,}
]
},
]
},
{
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"},
]
},
{
navText: "Resumes",
path: "resume",
children: [
{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"},
]
},
{navText: "Github", pubpath: "https://github.com/caperren"},
{navText: "LinkedIn", pubpath: "https://www.linkedin.com/in/caperren/"}
]
export const pathToMetadata = (path: string): navLink => {
let paths = path.split("/").filter((entry) => entry);
// Handle root path of /
if (paths.length < 1) {
paths = [""]
}
let currentEntries: navLink[] = siteLayout;
let foundEntry: navLink | undefined;
for (const path of paths) {
for (const currentEntry of currentEntries) {
if (currentEntry.path === path) {
foundEntry = currentEntry;
if (foundEntry.children && foundEntry.children.length > 0) {
currentEntries = foundEntry.children;
}
}
}
}
if (foundEntry === undefined) {
throw new Error(`${path} not found in site layout!`);
}
return foundEntry;
}
export const getPaths = (
currentEntries: navLink[] = siteLayout,
paths: string[] = [],
disabledOnly = false
): string[] => {
let foundPaths: string[] = [];
for (const currentEntry of currentEntries) {
if (currentEntry.children && currentEntry.children.length > 0) {
foundPaths = [
...foundPaths,
...getPaths(currentEntry.children, [...paths, currentEntry.path || ""], disabledOnly)
]
} else {
let enabled = currentEntry.enabled ?? true;
if (disabledOnly ? !enabled : enabled) {
foundPaths.push("/" + [...paths, currentEntry.path || ""].join("/"));
}
}
}
return [...new Set(foundPaths)];
}

1
src/env.d.ts vendored
View File

@@ -1,6 +1,5 @@
interface ImportMetaEnv {
readonly PUBLIC_REPO_VERSION_HASH: string;
readonly PUBLIC_PROJECT_VERSION: string;
readonly PUBLIC_BUILD_ENVIRONMENT: string;
}

View File

@@ -0,0 +1,13 @@
import { DateTime } from 'luxon';
export interface experience {
}
export interface subExperience {
name: string;
description: string;
startDate: DateTime;
endDate?: DateTime;
}

View File

@@ -0,0 +1,5 @@
export interface carouselGroup {
animation: "static" | "slide";
interval?: number;
images: ImageMetadata[];
}

View File

@@ -0,0 +1,7 @@
export interface navLink {
enabled?: boolean;
navText: string;
path?: string;
pubpath?: string;
children?: navLink[];
}

6
src/interfaces/table.ts Normal file
View File

@@ -0,0 +1,6 @@
export interface tableData {
header: string[];
columnPadding?: number;
rowPadding?: number;
rows: Array<Array<any>>;
}

View File

@@ -0,0 +1,6 @@
export interface timelineEntry {
event: string;
eventDetail?: string
date: string;
description?: string;
}

5
src/interfaces/video.ts Normal file
View File

@@ -0,0 +1,5 @@
export interface videoConfig {
videoTitle?: string
videoPath: string;
videoType?: string;
}

View File

@@ -1,23 +1,36 @@
---
import '../styles/global.css'
import Navbar from '../components/Navbar.astro';
import Footer from '../components/Footer.astro';
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.svg" type="image/svg+xml" />
<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>
<body class="bg-black text-white">
<Navbar/>
<main style="padding: 2rem;">
<slot/>
<main class="mx-6 mt-6 mb-14">
{(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

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

View File

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

View File

@@ -0,0 +1,10 @@
---
import BaseLayout from './BaseLayout.astro';
const resume = Astro.props.resume;
---
<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>
</BaseLayout>

View File

@@ -1,13 +0,0 @@
---
import BaseLayout from '../layouts/BaseLayout.astro';
---
<BaseLayout>
<div>Email:
<a href="mailto:caperren@caperren.com">caperren@caperren.com</a>
</div>
<div>
Phone:
<a href="tel:360-298-4396">360-298-4396</a>
</div>
</BaseLayout>

117
src/pages/education.astro Normal file
View File

@@ -0,0 +1,117 @@
---
import BaseLayout from "@layouts/BaseLayout.astro";
import Carousel from "@components/CustomCarousel/CustomCarousel.astro";
import Timeline from "@components/Timeline/Timeline.astro";
import Table from "@components/Table.astro";
import type {carouselGroup} from "@interfaces/image-carousel.ts";
import type {tableData} from "@interfaces/table.ts";
import type {timelineEntry} from "@interfaces/timeline.ts";
import fhhs_diploma from "@assets/education/fhhs-diploma.jpg";
import osu_bs_cs_diploma from "@assets/education/osu-bs-cs-diploma.jpg"
const diplomaCarouselGroup: carouselGroup = {
animation: "slide",
images: [
fhhs_diploma,
osu_bs_cs_diploma
]
}
const timeline: timelineEntry[] = [
{
event: "High School Diploma",
eventDetail: "Friday Harbor High School",
date: "June 2011"
},
{
event: "B.S. Computer Science",
eventDetail: "Oregon State University",
date: "June 2019"
},
];
const courseTable: tableData = {
header: ["Program", "Course", "Description"],
rows: [
["CS", "LDT", "INTRO TO LINUX"],
["CS", "261", "DATA STRUCTURES"],
["CS", "271", "COMPUTER ARCH & ASSEM LANGUAGE"],
["CS", "290", "WEB DEVELOPMENT"],
["CS", "312", "SYSTEM ADMINISTRATION"],
["CS", "325", "ANALYSIS OF ALGORITHMS"],
["CS", "331", "INTRO ARTIFICIAL INTELLIGENCE"],
["CS", "340", "INTRODUCTION TO DATABASES"],
["CS", "344", "OPERATING SYSTEMS I"],
["CS", "352", "INTRO TO USABILITY ENGINEERING"],
["CS", "361", "SOFTWARE ENGINEERING I"],
["CS", "362", "SOFTWARE ENGINEERING II"],
["CS", "370", "INTRODUCTION TO SECURITY"],
["CS", "372", "INTRO TO COMPUTER NETWORKS"],
["CS", "381", "PROGRAMMING LANGUAGE FUND"],
["CS", "391", "SOC & ETHICAL ISSUES IN COMSC"],
["CS", "444", "OPERATING SYSTEMS II"],
["CS", "461", "SENIOR SOFTWARE ENGR PROJECT I"],
["CS", "462", "SENIOR SOFTWARE ENGR PROJECT II"],
["CS", "463", "SENIOR SOFTWARE ENGR PROJECT III"],
["CS", "464", "OPEN SOURCE SOFTWARE"],
["CS", "468", "INCLUSIVE DESIGN (HCI)"],
["CS", "496", "MOBILE/CLOUD SOFTWARE DEVEL"],
["ECE", "111", "INTRODUCTION TO ECE: TOOLS"],
["ECE", "112", "INTRODUCTION TO ECE: CONCEPTS"],
["ECE", "151", "PROGRAMMING I/EMBED CONTR LAB"],
["ECE", "152", "PROGRAMMING II/EMBED CONTR LAB"],
["ECE", "271", "DIGITAL LOGIC DESIGN"],
["ECE", "272", "DIGITAL LOGIC DESIGN LAB"],
["ECE", "375", "COMPUTER ORG & ASSEMBLY LANG"],
["ENGR", "201", "ELECTRICAL FUNDAMENTALS I"],
["ENGR", "202", "ELECTRICAL FUNDAMENTALS II"],
["ENGR", "391", "ENGINEERING ECON & PROJ MGMT"],
["ROB", "421", "APPLIED ROBOTICS"],
["ROB", "456", "INTELLIGENT ROBOTS"],
["MTH", "231", "ELEMENTS DISCRETE MATH"],
["MTH", "241", "CALC FOR MGT & SOCIAL SCI"],
["MTH", "251", "DIFFERENTIAL CALCULUS"],
["MTH", "252", "INTEGRAL CALCULUS"],
["MTH", "254", "VECTOR CALCULUS I"],
["MTH", "306", "MATRIX & POWER SERIES METHODS"],
["ST", "314", "INTRO TO STATS FOR ENGINEERS"],
["PH", "211", "GENERAL PHYSICS WITH CALCULUS I"],
["PH", "212", "GENERAL PHYSICS WITH CALCULUS II"],
["PH", "213", "GENERAL PHYSICS WITH CALCULUS III"],
["WR", "LDT", "COMPOSITION III"],
["WR", "LDT", "CREATIVE WRITING"],
["WR", "121", "ENGLISH COMPOSITION"],
["WR", "214", "WRITING IN BUSINESS"],
["WR", "327", "TECHNICAL WRITING"],
["BI", "102", "GENERAL BIOLOGY"],
["BI", "349", "BIODIVERSITY: CAUSES, CONSERV"],
["CH", "201", "CHEMISTRY FOR ENGINEERING MAJ"],
["CH", "211", "RECITATION FOR CHEMISTRY 201"],
["COMM", "114", "ARGUMENT & CRITICAL DISCOURSE"],
["GEO", "LDT", "SURVEY EARTH SCIENCE"],
["HDFS", "240", "HUMAN SEXUALITY"],
["HHS", "231", "LIFETIME FITNESS FOR HEALTH"],
["HHS", "246", "LIFETIME FITNESS: WALKING"],
["MUS", "LDT", "LA: MUSIC APPRECIATION"],
["MUS", "108", "MUSIC CULTURES/ NAT AM FLUTE"],
["PAC", "123", "BOWLING I"],
["PHL", "205", "ETHICS"],
["PS", "LDT", "AMERICAN GOVT"],
["PSY", "201", "GENERAL PSYCHOLOGY"],
["QS", "262", "INTRODUCTION TO QUEER STUDIES"]
]
};
---
<BaseLayout title="Education">
<Carousel carouselGroup={diplomaCarouselGroup}/>
<h2 class="font-bold md:text-2xl my-4 underline">Timeline</h2>
<Timeline timeline={timeline}/>
<h2 class="font-bold md:text-2xl my-4 underline">Oregon State University</h2>
<a class="font-bold md:text-lg my-4 text-blue-500 underline hover:text-blue-300"
href="https://github.com/caperren/school_archives/tree/master/OSU%20Coursework">Coursework Archives</a>
<h3 class="font-bold md:text-lg my-4 underline">Course Listing</h3>
<Table data={courseTable}/>
</BaseLayout>

View File

@@ -0,0 +1,6 @@
---
import ExperienceLayout from "@layouts/ExperienceLayout.astro";
---
<ExperienceLayout title="CEOAS - LeConte Glacier Deployments">
</ExperienceLayout>

View File

@@ -0,0 +1,6 @@
---
import ExperienceLayout from "@layouts/ExperienceLayout.astro";
---
<ExperienceLayout title="CEOAS - Robotic Oceanographic Surface Sampler">
</ExperienceLayout>

View File

@@ -0,0 +1,6 @@
---
import ExperienceLayout from "@layouts/ExperienceLayout.astro";
---
<ExperienceLayout title="OSURC - Officer">
</ExperienceLayout>

View File

@@ -0,0 +1,6 @@
---
import ExperienceLayout from "@layouts/ExperienceLayout.astro";
---
<ExperienceLayout title="OSURC - Electrical Team Lead">
</ExperienceLayout>

View File

@@ -0,0 +1,6 @@
---
import ExperienceLayout from "@layouts/ExperienceLayout.astro";
---
<ExperienceLayout title="OSURC - Emergency Software Team Lead">
</ExperienceLayout>

View File

@@ -0,0 +1,245 @@
---
import ExperienceLayout from "@layouts/ExperienceLayout.astro";
import Carousel from "@components/CustomCarousel/CustomCarousel.astro";
import type {carouselGroup} from "@interfaces/image-carousel.ts";
import YtVideo from "@components/YtVideo.astro";
import type {videoConfig} from "@interfaces/video.ts";
const headerCarouselGroup: carouselGroup = {
animation: "slide",
images: []
}
const videoList: videoConfig[] = [
{
videoTitle: "Ground Station Software Quick Overview",
videoPath: "https://www.youtube-nocookie.com/embed/ZjGW-HWapVA"
},
{
videoTitle: "Rover Software Environment And Full Code Overview",
videoPath: "https://www.youtube-nocookie.com/embed/sceA2ZbEV8Y"
}
]
---
<ExperienceLayout title="OSURC - Software Team Lead">
<Carousel carouselGroup={headerCarouselGroup}/>
<h2 class="font-bold md:text-2xl my-4 underline">Ground Station Readouts & Features</h2>
<ul class="space-y-1 list-disc list-inside">
<li>Clock</li>
<li>Event Timer</li>
<li>Status Indication
<ul class="ps-5 space-y-1 list-disc list-inside">
<li>Rover Connection</li>
<li>Controller Connection Info</li>
<li>Radio Stats</li>
<li>GPS Stats</li>
<li>NVidia Jetson TX2 Computer Stats</li>
<li>Battery Voltage w/Low Battery Warning</li>
<li>Wheel Connections</li>
<li>Camera Connections</li>
</ul>
</li>
<li>Radio Direction Finding
<ul class="ps-5 space-y-1 list-disc list-inside">
<li>Raw Radio RSSI Indication</li>
<li>Radio RSSI Pulse Frequency w/Validity Indication</li>
</ul>
</li>
<li>Arm
<ul class="ps-5 space-y-1 list-disc list-inside">
<li>Special Movements
<ul class="ps-5 space-y-1 list-disc list-inside">
<li>Stow Arm</li>
<li>Unstow Arm</li>
<li>Upright Arm</li>
</ul>
</li>
<li>Calibrate Arm</li>
<li>Clear Arm Fault</li>
<li>Reset Arm Motor Drivers</li>
<li>Task Movements
<ul class="ps-5 space-y-1 list-disc list-inside">
<li>Approach Oxygen Tank</li>
<li>Depart Oxygen Tank</li>
<li>Approach Light Beacon</li>
<li>Depart Light Beacon</li>
</ul>
</li>
</ul>
</li>
<li>Mining/Science
<ul class="ps-5 space-y-1 list-disc list-inside">
<li>Bucket Weight Measurement</li>
<li>Bucket Lift/Tilt Position Readouts</li>
<li>Preset Bucket Movements
<ul class="ps-5 space-y-1 list-disc list-inside">
<li>Mining Transport</li>
<li>Mining Measure</li>
<li>Mining Scoop</li>
<li>Science Panorama</li>
<li>Mining Sample</li>
<li>Mining Probe</li>
</ul>
</li>
<li>Science Probe Readings
<ul class="ps-5 space-y-1 list-disc list-inside">
<li>Temp in C</li>
<li>Moisture %</li>
<li>Loss Tangent</li>
<li>Soil Electrical Conductivity</li>
<li>Real Dielectric Permitivity</li>
<li>Imaginary Dielectric Permitivity</li>
</ul>
</li>
<li>Science Camera Controls
<ul class="ps-5 space-y-1 list-disc list-inside">
<li>Video Output Selection
<ul class="ps-5 space-y-1 list-disc list-inside">
<li>Network Video</li>
<li>Camera LCD</li>
</ul>
</li>
<li>Photo Controls
<ul class="ps-5 space-y-1 list-disc list-inside">
<li>Zoom In One Step</li>
<li>Zoom Out One Step</li>
<li>Full Zoom In</li>
<li>Full Zoom Out</li>
<li>Shoot Photo</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li>SSH Console
<ul class="ps-5 space-y-1 list-disc list-inside">
<li>SSH Terminal Display</li>
<li>SSH Command Entry</li>
<li>Preset Commands
<ul class="ps-5 space-y-1 list-disc list-inside">
<li>Network Host Scan</li>
<li>List Wifi Networks</li>
<li>Equipment Login and Help</li>
<li>Equipment Logout</li>
<li>Equipment Status</li>
<li>Equipment Start</li>
<li>Equipment Stop</li>
</ul>
</li>
<li>Connect/Disconnect Rover Wifi by SSID</li>
</ul>
</li>
<li>Settings
<ul class="ps-5 space-y-1 list-disc list-inside">
<li>Map Selection</li>
<li>Map Zoom Level</li>
<li>Rover Wifi Radio Channel Selection</li>
</ul>
</li>
<li>Mapping Display
<ul class="ps-5 space-y-1 list-disc list-inside">
<li>Shows Google Map Terrain</li>
<li>Shows Rover Location And Orientation</li>
<li>Shows Rover GPS Coordinates</li>
<li>Shows Saved Waypoints</li>
</ul>
</li>
<li>Waypoint Entry / Editing
<ul class="ps-5 space-y-1 list-disc list-inside">
<li>Name Entry For Landmarks</li>
<li>GPS Entry in Decimal</li>
<li>GPS Entry in Degree/Minute/Second</li>
<li>Waypoint Color Choice</li>
</ul>
</li>
<li>Navigation Waypoints
<ul class="ps-5 space-y-1 list-disc list-inside">
<li>Shows And Allows Editing Of Nav Waypoints</li>
</ul>
</li>
<li>Landmark Waypoints
<ul class="ps-5 space-y-1 list-disc list-inside">
<li>Shows And Allows Editing Of Landmark Waypoints</li>
</ul>
</li>
<li>Arm Joint Positions
<ul class="ps-5 space-y-1 list-disc list-inside">
<li>Positions Of Six Arm Joints In Revolutions</li>
</ul>
</li>
<li>Gripper Joint Positions
<ul class="ps-5 space-y-1 list-disc list-inside">
<li>Positions Shown As Raw Encoder Positions</li>
</ul>
</li>
<li>Arm Motor Drive Statuses
<ul class="ps-5 space-y-1 list-disc list-inside">
<li>Communication/Movement/Fault Statuses For All Six Arm Joints</li>
</ul>
</li>
<li>Gripper Mode Readouts
<ul class="ps-5 space-y-1 list-disc list-inside">
<li>Gripper Mode Control State</li>
</ul>
</li>
<li>Xbox Control Mode
<ul class="ps-5 space-y-1 list-disc list-inside">
<li>Showed Whether Xbox Controller Moving Arm Or Mining</li>
</ul>
</li>
<li>Heading and Goal Indication w/Compass
<ul class="ps-5 space-y-1 list-disc list-inside">
<li>Raw Heading Indication</li>
<li>Goal Indication (Unused)</li>
<li>Compass Heading Indication</li>
</ul>
</li>
<li>Low Resolution Mode
<ul class="ps-5 space-y-1 list-disc list-inside">
<li>Controlled Low Resolution Fallback Mode During Radio Failure</li>
</ul>
</li>
<li>Current Speed
<ul class="ps-5 space-y-1 list-disc list-inside">
<li>GPS Speed</li>
</ul>
</li>
<li>Speed Limit
<ul class="ps-5 space-y-1 list-disc list-inside">
<li>% Of Max Rover Speed As Limit</li>
</ul>
</li>
<li>Tank Drive Output
<ul class="ps-5 space-y-1 list-disc list-inside">
<li>% Of Total Power To Left/Right Rover Drive Systems</li>
</ul>
</li>
<li>IMU Readings
<ul class="ps-5 space-y-1 list-disc list-inside">
<li>Pitch/Roll Readings In +/- 1 Readout</li>
</ul>
</li>
<li>Triple Camera Displays
<ul class="ps-5 space-y-1 list-disc list-inside">
<li>One Primary Video Display</li>
<li>Two Secondary Video Displays</li>
<li>Named Display For Currently Viewed Camera</li>
<li>Ability To Set Each Display To Any Camera</li>
<li>Ability to Disable Any Camera</li>
<li>Ability to Pan/Tilt Any Camera</li>
</ul>
</li>
</ul>
<h2 class="font-bold md:text-2xl my-4 underline">Rover Demos and Software Overviews</h2>
{videoList.map((video) => (
<h3 class="font-bold md:text-lg my-4">{video.videoTitle}</h3>
<YtVideo videoConfig={video}/>
))}
</ExperienceLayout>

View File

@@ -0,0 +1,6 @@
---
import ExperienceLayout from "@layouts/ExperienceLayout.astro";
---
<ExperienceLayout title="SARL - Dechorionator">
</ExperienceLayout>

View File

@@ -0,0 +1,6 @@
---
import ExperienceLayout from "@layouts/ExperienceLayout.astro";
---
<ExperienceLayout title="SARL - Denso Embryo Pick and Plate">
</ExperienceLayout>

View File

@@ -0,0 +1,6 @@
---
import ExperienceLayout from "@layouts/ExperienceLayout.astro";
---
<ExperienceLayout title="SARL - Shuttlebox Behavior System">
</ExperienceLayout>

View File

@@ -0,0 +1,6 @@
---
import ExperienceLayout from "@layouts/ExperienceLayout.astro";
---
<ExperienceLayout title="SARL - Team Lead">
</ExperienceLayout>

View File

@@ -0,0 +1,6 @@
---
import ExperienceLayout from "@layouts/ExperienceLayout.astro";
---
<ExperienceLayout title="SARL - Zebrafish Embryo Pick and Plate">
</ExperienceLayout>

Some files were not shown because too many files have changed in this diff Show More