Compare commits
37 Commits
aeffd1545f
...
43d0724345
| Author | SHA1 | Date | |
|---|---|---|---|
| 43d0724345 | |||
| c966f9302b | |||
| 47e10ba05f | |||
| 199074f10b | |||
| 4741b36003 | |||
| c3673b76b6 | |||
| d0f5838cac | |||
| 5c0152d234 | |||
| c6796e782a | |||
| 2d7f2904a8 | |||
| 6bb862e6e5 | |||
| 083d52d3a5 | |||
| 69c811854c | |||
| adcd336a16 | |||
| 4cd7e565eb | |||
| cf3b740907 | |||
| 297a56e7d6 | |||
| ee76972667 | |||
| 005e092344 | |||
| ffbc3beaf7 | |||
| 506987c164 | |||
| a807a39b09 | |||
| 40a673a418 | |||
| 9a4ecdd073 | |||
| d24a3054c1 | |||
| d1bc55e556 | |||
| f0319c4446 | |||
| 221587eff9 | |||
| 9ac3295127 | |||
| c5dde92023 | |||
| f91be707d9 | |||
| 7fe9303d5e | |||
| 33388f59ee | |||
| 3e34b94ec5 | |||
| 128dc14459 | |||
| d6e75ae2ea | |||
| 6f728ad146 |
27
.gitea/disabled_workflows/playwright.yml
Normal 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
|
||||||
@@ -4,7 +4,7 @@ on:
|
|||||||
branches: [main]
|
branches: [main]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
determine_version:
|
test:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
outputs:
|
outputs:
|
||||||
repo_name: ${{ steps.project_metadata.outputs.REPO_NAME }}
|
repo_name: ${{ steps.project_metadata.outputs.REPO_NAME }}
|
||||||
@@ -17,16 +17,29 @@ jobs:
|
|||||||
- name: Setup Node Environment
|
- name: Setup Node Environment
|
||||||
uses: actions/setup-node@v4
|
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
|
id: project_metadata
|
||||||
run: |
|
run: |
|
||||||
echo REPO_NAME=$(echo ${GITHUB_REPOSITORY} | awk -F"/" '{print $2}') >> $GITHUB_OUTPUT
|
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 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:
|
build_and_push:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: determine_version
|
needs: test
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout caperren-com Repository
|
- name: Checkout caperren-com Repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@@ -54,15 +67,8 @@ jobs:
|
|||||||
gitea.perren.cloud/caperren/caperren-com:latest
|
gitea.perren.cloud/caperren/caperren-com:latest
|
||||||
build-args: |
|
build-args: |
|
||||||
REPO_VERSION_HASH=${{ needs.determine_version.outputs.repo_version_hash }}
|
REPO_VERSION_HASH=${{ needs.determine_version.outputs.repo_version_hash }}
|
||||||
PROJECT_VERSION=${{ needs.determine_version.outputs.project_version }}
|
|
||||||
BUILD_ENVIRONMENT=production
|
BUILD_ENVIRONMENT=production
|
||||||
|
|
||||||
test:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: build_and_push
|
|
||||||
steps:
|
|
||||||
- run: echo "Placeholder"
|
|
||||||
|
|
||||||
deploy_production:
|
deploy_production:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: test
|
needs: test
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ on:
|
|||||||
types: [ opened, synchronize, reopened ]
|
types: [ opened, synchronize, reopened ]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
determine_version:
|
test:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
outputs:
|
outputs:
|
||||||
repo_name: ${{ steps.project_metadata.outputs.REPO_NAME }}
|
repo_name: ${{ steps.project_metadata.outputs.REPO_NAME }}
|
||||||
@@ -17,16 +17,29 @@ jobs:
|
|||||||
- name: Setup Node Environment
|
- name: Setup Node Environment
|
||||||
uses: actions/setup-node@v4
|
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
|
id: project_metadata
|
||||||
run: |
|
run: |
|
||||||
echo REPO_NAME=$(echo ${GITHUB_REPOSITORY} | awk -F"/" '{print $2}') >> $GITHUB_OUTPUT
|
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 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:
|
build_and_push:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: determine_version
|
needs: test
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout caperren-com Repository
|
- name: Checkout caperren-com Repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@@ -52,15 +65,8 @@ jobs:
|
|||||||
gitea.perren.cloud/caperren/caperren-com:latest-staging
|
gitea.perren.cloud/caperren/caperren-com:latest-staging
|
||||||
build-args: |
|
build-args: |
|
||||||
REPO_VERSION_HASH=${{ needs.determine_version.outputs.repo_version_hash }}
|
REPO_VERSION_HASH=${{ needs.determine_version.outputs.repo_version_hash }}
|
||||||
PROJECT_VERSION=${{ needs.determine_version.outputs.project_version }}
|
|
||||||
BUILD_ENVIRONMENT=staging
|
BUILD_ENVIRONMENT=staging
|
||||||
|
|
||||||
test:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: build_and_push
|
|
||||||
steps:
|
|
||||||
- run: echo "Placeholder"
|
|
||||||
|
|
||||||
deploy_staging:
|
deploy_staging:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: test
|
needs: test
|
||||||
|
|||||||
7
.gitignore
vendored
@@ -28,3 +28,10 @@ pnpm-debug.log*
|
|||||||
|
|
||||||
# jetbrains setting folder
|
# jetbrains setting folder
|
||||||
.idea/
|
.idea/
|
||||||
|
|
||||||
|
# Playwright
|
||||||
|
/test-results/
|
||||||
|
/playwright-report/
|
||||||
|
/blob-report/
|
||||||
|
/playwright/.cache/
|
||||||
|
/playwright/.auth/
|
||||||
|
|||||||
21
Dockerfile
@@ -8,27 +8,26 @@ COPY package.json package-lock.json tsconfig.json astro.config.mjs ./
|
|||||||
FROM base AS prod-deps
|
FROM base AS prod-deps
|
||||||
RUN npm install --omit=dev
|
RUN npm install --omit=dev
|
||||||
|
|
||||||
FROM base AS build-deps
|
FROM prod-deps AS build
|
||||||
RUN npm install
|
|
||||||
|
|
||||||
FROM build-deps AS build
|
|
||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
ARG REPO_VERSION_HASH
|
ARG REPO_VERSION_HASH
|
||||||
ARG BUILD_ENVIRONMENT
|
ARG BUILD_ENVIRONMENT
|
||||||
ARG PROJECT_VERSION
|
|
||||||
|
|
||||||
RUN echo "PUBLIC_REPO_VERSION_HASH=\"${REPO_VERSION_HASH}\" \n\
|
RUN echo "PUBLIC_REPO_VERSION_HASH=\"${REPO_VERSION_HASH}\" \n\
|
||||||
PUBLIC_BUILD_ENVIRONMENT=\"${BUILD_ENVIRONMENT}\" \n\
|
PUBLIC_BUILD_ENVIRONMENT=\"${BUILD_ENVIRONMENT}\"" >> .env
|
||||||
PUBLIC_PROJECT_VERSION=\"${PROJECT_VERSION}\"" >> .env
|
|
||||||
RUN npm run build
|
RUN npm run build
|
||||||
|
|
||||||
FROM httpd:latest AS runtime
|
FROM nginx:alpine AS runtime
|
||||||
WORKDIR /usr/local/apache2/htdocs
|
|
||||||
|
|
||||||
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
|
EXPOSE 80
|
||||||
|
|
||||||
|
#ENTRYPOINT ["/entrypoint.sh"]
|
||||||
6
Makefile
@@ -5,7 +5,8 @@
|
|||||||
fix \
|
fix \
|
||||||
astro_upgrade \
|
astro_upgrade \
|
||||||
build \
|
build \
|
||||||
dev
|
dev \
|
||||||
|
dev-hosted
|
||||||
|
|
||||||
default: dev
|
default: dev
|
||||||
|
|
||||||
@@ -24,3 +25,6 @@ build:
|
|||||||
dev:
|
dev:
|
||||||
npm run dev
|
npm run dev
|
||||||
|
|
||||||
|
dev-hosted:
|
||||||
|
npm run dev-hosted
|
||||||
|
|
||||||
|
|||||||
@@ -1,18 +1,25 @@
|
|||||||
// @ts-check
|
// @ts-check
|
||||||
import { defineConfig } from 'astro/config';
|
import {defineConfig} from 'astro/config';
|
||||||
import rehypeAstroRelativeMarkdownLinks from "astro-rehype-relative-markdown-links";
|
import sitemap from "@astrojs/sitemap";
|
||||||
|
|
||||||
import tailwindcss from "@tailwindcss/vite";
|
import tailwindcss from "@tailwindcss/vite";
|
||||||
|
|
||||||
|
import {siteLayout, getPaths} from "./src/data/site-layout.ts";
|
||||||
|
|
||||||
|
const disabledPaths = getPaths(siteLayout, [], true)
|
||||||
|
|
||||||
// https://astro.build/config
|
// https://astro.build/config
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
integrations: [],
|
site: "https://caperren.com",
|
||||||
|
prefetch: {
|
||||||
markdown: {
|
prefetchAll: true
|
||||||
rehypePlugins: [rehypeAstroRelativeMarkdownLinks],
|
},
|
||||||
},
|
integrations: [
|
||||||
|
sitemap({
|
||||||
vite: {
|
filter: (pagePath) =>
|
||||||
plugins: [tailwindcss()],
|
!disabledPaths.some(disabledPath => pagePath.includes(disabledPath))
|
||||||
},
|
})
|
||||||
|
],
|
||||||
|
vite: {
|
||||||
|
plugins: [tailwindcss()],
|
||||||
|
},
|
||||||
});
|
});
|
||||||
14
entrypoint.sh
Executable 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
@@ -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
22
package.json
@@ -4,15 +4,27 @@
|
|||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "astro dev",
|
"dev": "astro dev",
|
||||||
|
"dev-hosted": "astro dev --host",
|
||||||
"build": "astro build",
|
"build": "astro build",
|
||||||
"preview": "astro preview",
|
"preview": "astro preview",
|
||||||
"astro": "astro"
|
"astro": "astro",
|
||||||
|
"test": "vitest",
|
||||||
|
"e2e-test": "playwright test"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@astrojs/sitemap": "^3.6.0",
|
||||||
"@tailwindcss/vite": "^4.1.11",
|
"@tailwindcss/vite": "^4.1.11",
|
||||||
"astro": "^5.11.0",
|
"astro": "^5.15.4",
|
||||||
"astro-navbar": "^2.4.0",
|
"flowbite": "^3.1.2",
|
||||||
"astro-rehype-relative-markdown-links": "^0.18.1",
|
"leader-line-new": "^1.1.9",
|
||||||
"tailwindcss": "^4.1.11"
|
"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
@@ -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
|
After Width: | Height: | Size: 662 B |
@@ -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 |
BIN
src/assets/about/alaska-bike-mountain-ocean.jpg
Normal file
|
After Width: | Height: | Size: 2.4 MiB |
BIN
src/assets/about/circ-champions.jpg
Normal file
|
After Width: | Height: | Size: 2.7 MiB |
|
Before Width: | Height: | Size: 956 KiB After Width: | Height: | Size: 956 KiB |
BIN
src/assets/education/fhhs-diploma.jpg
Normal file
|
After Width: | Height: | Size: 3.7 MiB |
BIN
src/assets/education/osu-bs-cs-diploma.jpg
Normal file
|
After Width: | Height: | Size: 5.0 MiB |
|
Before Width: | Height: | Size: 124 KiB After Width: | Height: | Size: 124 KiB |
|
Before Width: | Height: | Size: 1.9 MiB After Width: | Height: | Size: 1.9 MiB |
BIN
src/assets/hobby/body-mods/rfid-implant/injection-site.jpg
Normal file
|
After Width: | Height: | Size: 288 KiB |
BIN
src/assets/hobby/body-mods/rfid-implant/injector-exploded.jpg
Normal file
|
After Width: | Height: | Size: 9.4 KiB |
|
After Width: | Height: | Size: 3.8 MiB |
BIN
src/assets/hobby/body-mods/rfid-implant/xem-pouch.jpg
Normal file
|
After Width: | Height: | Size: 3.0 MiB |
BIN
src/assets/hobby/homelab/home-server-rack/rack-from-above.jpg
Normal file
|
After Width: | Height: | Size: 2.2 MiB |
BIN
src/assets/hobby/homelab/home-server-rack/rack-from-below.jpg
Normal file
|
After Width: | Height: | Size: 2.1 MiB |
BIN
src/assets/hobby/homelab/home-server-rack/rack-middle.jpg
Normal file
|
After Width: | Height: | Size: 2.0 MiB |
BIN
src/assets/hobby/homelab/home-server-rack/rack-top.jpg
Normal file
|
After Width: | Height: | Size: 2.3 MiB |
|
After Width: | Height: | Size: 721 KiB |
|
After Width: | Height: | Size: 1.5 MiB |
BIN
src/assets/hobby/homelab/offsite-backup-rack/enclosure-front.jpg
Normal file
|
After Width: | Height: | Size: 406 KiB |
BIN
src/assets/hobby/homelab/offsite-backup-rack/enclosure-left.jpg
Normal file
|
After Width: | Height: | Size: 344 KiB |
BIN
src/assets/hobby/homelab/offsite-backup-rack/enclosure-rear.jpg
Normal file
|
After Width: | Height: | Size: 382 KiB |
BIN
src/assets/hobby/homelab/offsite-backup-rack/enclosure-right.jpg
Normal file
|
After Width: | Height: | Size: 317 KiB |
|
After Width: | Height: | Size: 2.1 MiB |
|
After Width: | Height: | Size: 922 KiB |
|
After Width: | Height: | Size: 2.2 MiB |
|
After Width: | Height: | Size: 2.4 MiB |
BIN
src/assets/hobby/homelab/offsite-backup-rack/sata-tight-fit.jpg
Normal file
|
After Width: | Height: | Size: 2.0 MiB |
|
After Width: | Height: | Size: 2.3 MiB |
BIN
src/assets/hobby/homelab/offsite-backup-rack/up-and-running.png
Normal file
|
After Width: | Height: | Size: 186 KiB |
|
After Width: | Height: | Size: 2.7 MiB |
|
After Width: | Height: | Size: 1.8 MiB |
|
After Width: | Height: | Size: 1.8 MiB |
|
After Width: | Height: | Size: 1.5 MiB |
|
After Width: | Height: | Size: 1.5 MiB |
|
After Width: | Height: | Size: 1.8 MiB |
|
After Width: | Height: | Size: 2.2 MiB |
|
After Width: | Height: | Size: 2.4 MiB |
|
After Width: | Height: | Size: 1.6 MiB |
|
After Width: | Height: | Size: 3.2 MiB |
|
After Width: | Height: | Size: 2.0 MiB |
BIN
src/assets/hobby/motorcycling/lineup/1999-kawasaki-concours.jpg
Normal file
|
After Width: | Height: | Size: 4.7 MiB |
BIN
src/assets/hobby/motorcycling/lineup/2005-suzuki-drz-400.jpg
Normal file
|
After Width: | Height: | Size: 5.2 MiB |
|
After Width: | Height: | Size: 21 MiB |
BIN
src/assets/hobby/motorcycling/lineup/2021-csc-sg400.jpg
Normal file
|
After Width: | Height: | Size: 731 KiB |
BIN
src/assets/logo-title-large.png
Normal file
|
After Width: | Height: | Size: 41 KiB |
@@ -2,13 +2,14 @@
|
|||||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
<svg
|
<svg
|
||||||
|
class="logo-title"
|
||||||
width="21.129419mm"
|
width="21.129419mm"
|
||||||
height="9.3295746mm"
|
height="9.3295746mm"
|
||||||
viewBox="0 0 74.868021 33.057548"
|
viewBox="0 0 74.868021 33.057548"
|
||||||
id="svg4376"
|
id="svg4376"
|
||||||
version="1.1"
|
version="1.1"
|
||||||
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
|
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:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 5.6 KiB |
@@ -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>
|
|
||||||
96
src/components/CustomCarousel/CustomCarousel.astro
Normal 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"/>
|
||||||
80
src/components/CustomCarousel/custom-carousel.ts
Normal 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)
|
||||||
@@ -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>
|
</footer>
|
||||||
@@ -1,150 +1,34 @@
|
|||||||
---
|
---
|
||||||
import {Image} from 'astro:assets';
|
import NestedNavbarEntry from "@components/NestedNavbarEntries.astro";
|
||||||
import {Astronav, MenuItems, MenuIcon, Dropdown, DropdownItems} from "astro-navbar";
|
|
||||||
|
|
||||||
import logoTitle from "../assets/logo-title.svg";
|
import logo_title_large from "@assets/logo-title-large.png";
|
||||||
|
import {siteLayout} from "@data/site-layout.ts";
|
||||||
const navItems = [
|
import {Image} from "astro:assets";
|
||||||
{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'}
|
|
||||||
];
|
|
||||||
---
|
---
|
||||||
|
|
||||||
<header class="navbar md:flex p-5 gap-5">
|
|
||||||
<Astronav>
|
<nav class="border-b-4 border-b-caperren-green text-caperren-green">
|
||||||
<div class="flex w-full justify-between">
|
<div class="flex flex-wrap items-center justify-between mx-auto p-6">
|
||||||
<a href="/">
|
<a href="/">
|
||||||
<Image src={logoTitle} alt="Logo Title" height="50" loading="eager"/>
|
<Image src={logo_title_large}
|
||||||
</a>
|
height="56"
|
||||||
<div class="block md:hidden">
|
width="139"
|
||||||
<MenuIcon class="w-4 h-4 text-gray-800"/>
|
class="h-14 w-auto"
|
||||||
</div>
|
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>
|
</div>
|
||||||
<MenuItems class="hidden md:flex">
|
</div>
|
||||||
<ul class="flex flex-col md:flex-row md:gap-5">
|
</nav>
|
||||||
<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>
|
|
||||||
|
|||||||
51
src/components/NestedNavbarEntries.astro
Normal 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>
|
||||||
34
src/components/Table.astro
Normal 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>
|
||||||
34
src/components/Timeline/Timeline.astro
Normal 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"/>
|
||||||
44
src/components/Timeline/timeline.ts
Normal 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)
|
||||||
12
src/components/Video.astro
Normal 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>
|
||||||
|
|
||||||
9
src/components/YtVideo.astro
Normal 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
@@ -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
@@ -1,6 +1,5 @@
|
|||||||
interface ImportMetaEnv {
|
interface ImportMetaEnv {
|
||||||
readonly PUBLIC_REPO_VERSION_HASH: string;
|
readonly PUBLIC_REPO_VERSION_HASH: string;
|
||||||
readonly PUBLIC_PROJECT_VERSION: string;
|
|
||||||
readonly PUBLIC_BUILD_ENVIRONMENT: string;
|
readonly PUBLIC_BUILD_ENVIRONMENT: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
13
src/interfaces/experience-metadata.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { DateTime } from 'luxon';
|
||||||
|
|
||||||
|
export interface experience {
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
export interface subExperience {
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
|
||||||
|
startDate: DateTime;
|
||||||
|
endDate?: DateTime;
|
||||||
|
}
|
||||||
5
src/interfaces/image-carousel.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
export interface carouselGroup {
|
||||||
|
animation: "static" | "slide";
|
||||||
|
interval?: number;
|
||||||
|
images: ImageMetadata[];
|
||||||
|
}
|
||||||
7
src/interfaces/site-layout.ts
Normal 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
@@ -0,0 +1,6 @@
|
|||||||
|
export interface tableData {
|
||||||
|
header: string[];
|
||||||
|
columnPadding?: number;
|
||||||
|
rowPadding?: number;
|
||||||
|
rows: Array<Array<any>>;
|
||||||
|
}
|
||||||
6
src/interfaces/timeline.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
export interface timelineEntry {
|
||||||
|
event: string;
|
||||||
|
eventDetail?: string
|
||||||
|
date: string;
|
||||||
|
description?: string;
|
||||||
|
}
|
||||||
5
src/interfaces/video.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
export interface videoConfig {
|
||||||
|
videoTitle?: string
|
||||||
|
videoPath: string;
|
||||||
|
videoType?: string;
|
||||||
|
}
|
||||||
@@ -1,23 +1,36 @@
|
|||||||
---
|
---
|
||||||
import '../styles/global.css'
|
import '@styles/global.css'
|
||||||
import Navbar from '../components/Navbar.astro';
|
|
||||||
import Footer from '../components/Footer.astro';
|
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 pageTitle = Astro.props.title ? `${Astro.props.title} - Corwin Perren` : "Corwin Perren";
|
||||||
|
const showTitle = Astro.props.showTitle ?? true;
|
||||||
|
const pageEnabled = pathToMetadata(Astro.url.pathname).enabled ?? true;
|
||||||
---
|
---
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8"/>
|
<meta charset="UTF-8"/>
|
||||||
<link rel="icon" href="/favicon.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"/>
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||||
<title>{pageTitle}</title>
|
<title>{pageEnabled ? pageTitle : "Corwin Perren"}</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body class="bg-black text-white">
|
||||||
<Navbar/>
|
<Navbar/>
|
||||||
<main style="padding: 2rem;">
|
<main class="mx-6 mt-6 mb-14">
|
||||||
<slot/>
|
{(Astro.props.title && showTitle && pageEnabled) && (
|
||||||
|
<h1 class="font-extrabold md:text-3xl md:mb-6">{Astro.props.title}</h1>
|
||||||
|
)}
|
||||||
|
{pageEnabled && (
|
||||||
|
<slot/>
|
||||||
|
)}
|
||||||
</main>
|
</main>
|
||||||
<Footer/>
|
<Footer/>
|
||||||
|
<script src="../scripts/main.ts"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
import BaseLayout from './BaseLayout.astro';
|
import BaseLayout from './BaseLayout.astro';
|
||||||
---
|
---
|
||||||
<BaseLayout>
|
<BaseLayout title={Astro.props.title}>
|
||||||
<slot/>
|
<slot/>
|
||||||
</BaseLayout>
|
</BaseLayout>
|
||||||
6
src/layouts/HobbyLayout.astro
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
import BaseLayout from './BaseLayout.astro';
|
||||||
|
---
|
||||||
|
<BaseLayout title={Astro.props.title}>
|
||||||
|
<slot/>
|
||||||
|
</BaseLayout>
|
||||||
10
src/layouts/ResumeLayout.astro
Normal 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>
|
||||||
@@ -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
@@ -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>
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
import ExperienceLayout from "@layouts/ExperienceLayout.astro";
|
||||||
|
---
|
||||||
|
|
||||||
|
<ExperienceLayout title="CEOAS - LeConte Glacier Deployments">
|
||||||
|
</ExperienceLayout>
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
import ExperienceLayout from "@layouts/ExperienceLayout.astro";
|
||||||
|
---
|
||||||
|
|
||||||
|
<ExperienceLayout title="CEOAS - Robotic Oceanographic Surface Sampler">
|
||||||
|
</ExperienceLayout>
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
import ExperienceLayout from "@layouts/ExperienceLayout.astro";
|
||||||
|
---
|
||||||
|
|
||||||
|
<ExperienceLayout title="OSURC - Officer">
|
||||||
|
</ExperienceLayout>
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
import ExperienceLayout from "@layouts/ExperienceLayout.astro";
|
||||||
|
---
|
||||||
|
|
||||||
|
<ExperienceLayout title="OSURC - Electrical Team Lead">
|
||||||
|
</ExperienceLayout>
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
import ExperienceLayout from "@layouts/ExperienceLayout.astro";
|
||||||
|
---
|
||||||
|
|
||||||
|
<ExperienceLayout title="OSURC - Emergency Software Team Lead">
|
||||||
|
</ExperienceLayout>
|
||||||
@@ -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>
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
import ExperienceLayout from "@layouts/ExperienceLayout.astro";
|
||||||
|
---
|
||||||
|
|
||||||
|
<ExperienceLayout title="SARL - Dechorionator">
|
||||||
|
</ExperienceLayout>
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
import ExperienceLayout from "@layouts/ExperienceLayout.astro";
|
||||||
|
---
|
||||||
|
|
||||||
|
<ExperienceLayout title="SARL - Denso Embryo Pick and Plate">
|
||||||
|
</ExperienceLayout>
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
import ExperienceLayout from "@layouts/ExperienceLayout.astro";
|
||||||
|
---
|
||||||
|
|
||||||
|
<ExperienceLayout title="SARL - Shuttlebox Behavior System">
|
||||||
|
</ExperienceLayout>
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
import ExperienceLayout from "@layouts/ExperienceLayout.astro";
|
||||||
|
---
|
||||||
|
|
||||||
|
<ExperienceLayout title="SARL - Team Lead">
|
||||||
|
</ExperienceLayout>
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
import ExperienceLayout from "@layouts/ExperienceLayout.astro";
|
||||||
|
---
|
||||||
|
|
||||||
|
<ExperienceLayout title="SARL - Zebrafish Embryo Pick and Plate">
|
||||||
|
</ExperienceLayout>
|
||||||