feat: add project pages

This commit is contained in:
Alexander Daichendt 2023-09-27 23:36:46 +02:00
parent 5fc58fd0bc
commit d41a23cfcf
16 changed files with 171 additions and 28 deletions

View file

@ -5,6 +5,7 @@
const NAV_ITEMS = [
{ href: '/', label: 'Home' },
{ href: '/blog', label: 'Blog' },
{ href: '/projects', label: 'Projects' },
{ href: '/contact', label: 'Contact' },
];
</script>

View file

@ -30,10 +30,11 @@
{...props}
{href}
>
<span class="text"><slot /></span>
{#if !disableIcon && !internal}
<Icon path={internal ? mdiChevronRight : mdiLinkVariant} size="1rem" />
{/if}
<span class="text"><slot /></span>
</a><style>
a {
color: var(--special-color);

View file

@ -33,13 +33,19 @@
<SEO {title} {description} {keywords} />
<h1>{title}</h1>
<aside role="note">
{#if updated}
<Icon path={mdiPencil} size="0.8rem" /> updated {new Date(updated).toLocaleDateString('en-GB')};
{/if}
<Icon path={mdiCalendar} size="0.8rem" /> created
{new Date(created).toLocaleDateString('en-GB')}
</aside>
{#if updated || created}
<aside role="note">
{#if updated}
<Icon path={mdiPencil} size="0.8rem" /> updated {new Date(updated).toLocaleDateString(
'en-GB',
)};
{/if}
{#if created}
<Icon path={mdiCalendar} size="0.8rem" /> created
{new Date(created).toLocaleDateString('en-GB')}
{/if}
</aside>
{/if}
<Divider />
<slot />

View file

View file

@ -10,6 +10,17 @@ export interface BlogPostFrontmatter {
export interface BlogPostMeta extends BlogPostFrontmatter {
href: string;
}
export interface ProjectsFrontmatter {
title: string;
description: string;
keywords: string[];
inProgress: boolean;
}
export interface ProjectMeta extends ProjectsFrontmatter {
href: string;
}
export interface Skill {
name: string;
years: number;

View file

@ -23,7 +23,7 @@
}
:global(.container) {
max-width: 70rem;
max-width: 50rem;
margin: 0 auto;
padding: 0 1rem;
overflow-x: hidden;

View file

@ -60,12 +60,18 @@
<p>I am a software engineer, Linux enthusiast and a friend of lightweight, resilient systems.</p>
<p>
Programming has been a hobby of mine since my teens. Been working on countless projects for
various games. For a few years now I am maintaining a small homelab, which got me into DevOps/SRE.
I am a privacy enthusiast and advocate for non-invasive software. Sometimes, I build slick
websites that do not load megabytes of data and try to follow best-practices. Currently, I am
working on my Masters degree in computer science at <Link href="https://www.tum.de/">TUM</Link>.
My journey in the tech world has been a dynamic one. I've immersed myself in countless projects
spanning various video games and, for the past few years, have been maintaining a small homelab,
which ignited my passion for DevOps / SRE. I am a privacy enthusiast and advocate for non-invasive
software. Occasionally, I channel my creativity into building sleek web applications that
prioritize efficiency and adhere to web standards and best practices.
</p>
<p>
Currently, I'm pursuing a Master's degree in computer science at <Link href="https://www.tum.de/"
>TUM</Link
>, where I successfully contribute to numerous research papers.
</p>
<h2>Skills</h2>
<ul>

View file

@ -2,7 +2,7 @@ export async function load() {
return {
seo: {
title: 'Home',
description: 'Alex Daichendt"s website, blog, and yard of stuffs and things of modern tech.',
description: 'Alex Daichendt"s website, blog, and collection of thoughts on modern tech.',
},
};
}

View file

@ -0,0 +1,22 @@
import type { ProjectMeta, ProjectsFrontmatter } from '$lib/utils/types';
import type { PageServerLoad } from './$types';
const removeExtension = (path: string) => path.replace(/\.[^.]*$/g, '').replace('/+page', '');
export const load: PageServerLoad = async () => {
const modulesSVX = import.meta.glob('./**/*.svx');
const modulesMD = import.meta.glob('./**/*.md');
const modules = { ...modulesMD, ...modulesSVX };
const projects: ProjectMeta[] = [];
const resolved = (await Promise.all(Object.values(modules).map((f) => f()))) as {
metadata: ProjectsFrontmatter;
}[];
resolved.forEach((file, index) => {
const path = Object.keys(modules)[index];
const { metadata } = file;
projects.push({ ...metadata, href: `projects/${removeExtension(path)}` });
});
return { projects };
};

View file

@ -0,0 +1,22 @@
<script lang="ts">
import Link from '$components/Link.svelte';
import ListItem from '$components/ListItem.svelte';
import Seo from '$components/SEO.svelte';
import type { PageData } from './$types';
export let data: PageData;
$: projects = data.projects;
</script>
<Seo />
<h1>Projects</h1>
<p>Detailed descriptions of my contributions to various projects.</p>
<ul>
{#each projects as project}
<ListItem>
<Link href={project.href}>{project.title}</Link>
</ListItem>
{/each}
</ul>

View file

@ -0,0 +1,11 @@
import type { PageLoad } from './$types';
export const load: PageLoad = ({ data }) => {
return {
projects: data.projects,
seo: {
title: 'Projects',
description: 'Detailed descriptions of projects created by Alex Daichendt',
},
};
};

View file

@ -0,0 +1,61 @@
---
title: 'GW2 Gear Optimizer'
description: 'The Gear Optimizer helps Guild Wars 2 players find optimal builds for fractals, raids, and strike missions.'
keywords:
- gw2
- guild wars 2
- optimizer
layout: blog
---
<script>
import overview from "./images/gear-optimizer.png?default"
import Image from "$components/Image.svelte"
</script>
## TL;DR
{description}
- Demo / live site: [https://optimizer.discretize.eu](https://optimizer.discretize.eu)
- Code: [Github](https://github.com/discretize/discretize-gear-optimizer)
I made signifcant contributions to the frontend as well as to the calculation core by rewriting it in Rust and implementing threading.
Furthermore, I worked closely with and coordinated a small team of a few developers from all over the world ([see here](https://github.com/discretize/discretize-gear-optimizer/graphs/contributors)) to improve the project further.
<Image meta={overview} />
## Description
Interesting features for players of the video game:
- find optimal builds based on various parameters
- build templates with extensive sensible defaults that are used by players
- support for different game modes with different balancing
- Uptime input for conditional buffs
- keyboard shortcuts
- custom arbitrary modifier input to allow simulating theoretical balancing
- infusion helper: calculates the cheapest way to acquire n agony resistance
- input condition distribution
- displays the results just like in game
- share settings and results with a single link
## Most Significant Contributions
- I rewrote the frontend from scratch utilizing modern JS tooling
-> the old optimizer consisted out of one 10k LoC HTML file and an equally large js file; bundled with gulp and jquery ...
- ported the calculation core to Rust; refactored the combination generation code to support multi-threading
-> added some heuristics to reduce the amount of combinations
- state compression algorithm based on a schema so that players can share links to their builds without a server storing data
## Technical Details
The optimizer has numerous interesting technical features:
- React SPA without SSR with Vite
- Built with Material-UI 5 and Emotion for CSS-in-JS
- Statemanagement with ReduxJS
- multithreaded calculations with Rust compiled to WASM and WebWorkers
- i18n localization (Chinese and German)
- hosted on Cloudflare pages utilizing Workers and KV
- custom algorithm for lossless state compress based on a schema into base64, url-save strings

Binary file not shown.

After

Width:  |  Height:  |  Size: 568 KiB