feat: add project pages
This commit is contained in:
parent
5fc58fd0bc
commit
d41a23cfcf
16 changed files with 171 additions and 28 deletions
10
package.json
10
package.json
|
|
@ -1,7 +1,12 @@
|
||||||
{
|
{
|
||||||
"name": "myblog",
|
"name": "daichendt.one",
|
||||||
"version": "0.0.1",
|
"version": "1.0.0",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"author": {
|
||||||
|
"email": "me@daichendt.one",
|
||||||
|
"name": "Alex Daichendt"
|
||||||
|
},
|
||||||
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite dev",
|
"dev": "vite dev",
|
||||||
"build": "vite build",
|
"build": "vite build",
|
||||||
|
|
@ -43,7 +48,6 @@
|
||||||
"vite": "^4.0.3",
|
"vite": "^4.0.3",
|
||||||
"vite-imagetools": "^4.0.12"
|
"vite-imagetools": "^4.0.12"
|
||||||
},
|
},
|
||||||
"type": "module",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fontsource/ubuntu-mono": "^4.5.11",
|
"@fontsource/ubuntu-mono": "^4.5.11",
|
||||||
"@mdi/js": "^7.1.96",
|
"@mdi/js": "^7.1.96",
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
const NAV_ITEMS = [
|
const NAV_ITEMS = [
|
||||||
{ href: '/', label: 'Home' },
|
{ href: '/', label: 'Home' },
|
||||||
{ href: '/blog', label: 'Blog' },
|
{ href: '/blog', label: 'Blog' },
|
||||||
|
{ href: '/projects', label: 'Projects' },
|
||||||
{ href: '/contact', label: 'Contact' },
|
{ href: '/contact', label: 'Contact' },
|
||||||
];
|
];
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -30,10 +30,11 @@
|
||||||
{...props}
|
{...props}
|
||||||
{href}
|
{href}
|
||||||
>
|
>
|
||||||
|
<span class="text"><slot /></span>
|
||||||
|
|
||||||
{#if !disableIcon && !internal}
|
{#if !disableIcon && !internal}
|
||||||
<Icon path={internal ? mdiChevronRight : mdiLinkVariant} size="1rem" />
|
<Icon path={internal ? mdiChevronRight : mdiLinkVariant} size="1rem" />
|
||||||
{/if}
|
{/if}
|
||||||
<span class="text"><slot /></span>
|
|
||||||
</a><style>
|
</a><style>
|
||||||
a {
|
a {
|
||||||
color: var(--special-color);
|
color: var(--special-color);
|
||||||
|
|
|
||||||
|
|
@ -33,13 +33,19 @@
|
||||||
<SEO {title} {description} {keywords} />
|
<SEO {title} {description} {keywords} />
|
||||||
|
|
||||||
<h1>{title}</h1>
|
<h1>{title}</h1>
|
||||||
<aside role="note">
|
{#if updated || created}
|
||||||
{#if updated}
|
<aside role="note">
|
||||||
<Icon path={mdiPencil} size="0.8rem" /> updated {new Date(updated).toLocaleDateString('en-GB')};
|
{#if updated}
|
||||||
{/if}
|
<Icon path={mdiPencil} size="0.8rem" /> updated {new Date(updated).toLocaleDateString(
|
||||||
<Icon path={mdiCalendar} size="0.8rem" /> created
|
'en-GB',
|
||||||
{new Date(created).toLocaleDateString('en-GB')}
|
)};
|
||||||
</aside>
|
{/if}
|
||||||
|
{#if created}
|
||||||
|
<Icon path={mdiCalendar} size="0.8rem" /> created
|
||||||
|
{new Date(created).toLocaleDateString('en-GB')}
|
||||||
|
{/if}
|
||||||
|
</aside>
|
||||||
|
{/if}
|
||||||
<Divider />
|
<Divider />
|
||||||
<slot />
|
<slot />
|
||||||
|
|
||||||
|
|
|
||||||
0
src/lib/layouts/projects.svelte
Normal file
0
src/lib/layouts/projects.svelte
Normal file
|
|
@ -10,6 +10,17 @@ export interface BlogPostFrontmatter {
|
||||||
export interface BlogPostMeta extends BlogPostFrontmatter {
|
export interface BlogPostMeta extends BlogPostFrontmatter {
|
||||||
href: string;
|
href: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ProjectsFrontmatter {
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
keywords: string[];
|
||||||
|
inProgress: boolean;
|
||||||
|
}
|
||||||
|
export interface ProjectMeta extends ProjectsFrontmatter {
|
||||||
|
href: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface Skill {
|
export interface Skill {
|
||||||
name: string;
|
name: string;
|
||||||
years: number;
|
years: number;
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
:global(.container) {
|
:global(.container) {
|
||||||
max-width: 70rem;
|
max-width: 50rem;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
padding: 0 1rem;
|
padding: 0 1rem;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
|
|
|
||||||
|
|
@ -60,12 +60,18 @@
|
||||||
|
|
||||||
<p>I am a software engineer, Linux enthusiast and a friend of lightweight, resilient systems.</p>
|
<p>I am a software engineer, Linux enthusiast and a friend of lightweight, resilient systems.</p>
|
||||||
<p>
|
<p>
|
||||||
Programming has been a hobby of mine since my teens. Been working on countless projects for
|
My journey in the tech world has been a dynamic one. I've immersed myself in countless projects
|
||||||
various games. For a few years now I am maintaining a small homelab, which got me into DevOps/SRE.
|
spanning various video games and, for the past few years, have been maintaining a small homelab,
|
||||||
I am a privacy enthusiast and advocate for non-invasive software. Sometimes, I build slick
|
which ignited my passion for DevOps / SRE. I am a privacy enthusiast and advocate for non-invasive
|
||||||
websites that do not load megabytes of data and try to follow best-practices. Currently, I am
|
software. Occasionally, I channel my creativity into building sleek web applications that
|
||||||
working on my Masters degree in computer science at <Link href="https://www.tum.de/">TUM</Link>.
|
prioritize efficiency and adhere to web standards and best practices.
|
||||||
</p>
|
</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>
|
<h2>Skills</h2>
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ export async function load() {
|
||||||
return {
|
return {
|
||||||
seo: {
|
seo: {
|
||||||
title: 'Home',
|
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.',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
22
src/routes/projects/+page.server.ts
Normal file
22
src/routes/projects/+page.server.ts
Normal 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 };
|
||||||
|
};
|
||||||
22
src/routes/projects/+page.svelte
Normal file
22
src/routes/projects/+page.svelte
Normal 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>
|
||||||
11
src/routes/projects/+page.ts
Normal file
11
src/routes/projects/+page.ts
Normal 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',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
61
src/routes/projects/gw2-gear-optimizer/+page.md
Normal file
61
src/routes/projects/gw2-gear-optimizer/+page.md
Normal 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
|
||||||
BIN
src/routes/projects/gw2-gear-optimizer/images/gear-optimizer.png
Normal file
BIN
src/routes/projects/gw2-gear-optimizer/images/gear-optimizer.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 568 KiB |
|
|
@ -1,3 +1,6 @@
|
||||||
User-agent: *
|
User-agent: *
|
||||||
Disallow: /contact
|
Disallow: /contact
|
||||||
Disallow: /admin
|
Disallow: /admin
|
||||||
|
|
||||||
|
User-agent: GPTBot
|
||||||
|
Disallow: /
|
||||||
13
yarn.lock
13
yarn.lock
|
|
@ -551,15 +551,10 @@ callsites@^3.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
|
resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
|
||||||
integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
|
integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
|
||||||
|
|
||||||
caniuse-lite@^1.0.30001400:
|
caniuse-lite@^1.0.30001400, caniuse-lite@^1.0.30001426:
|
||||||
version "1.0.30001411"
|
version "1.0.30001534"
|
||||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001411.tgz#303c8594ca5903b193a6d875ac613548cb73379a"
|
resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001534.tgz"
|
||||||
integrity sha512-HPnJKESKuhKpHvMY1/ux7J3nG7xG8jRuL4lbyCjDRm0doTNV91tcRk60xrP7Ym9DtJH/yuqntDWBJCqpXB4b7g==
|
integrity sha512-vlPVrhsCS7XaSh2VvWluIQEzVhefrUQcEsQWSS5A5V+dM07uv1qHeQzAOTGIMy9i3e9bH15+muvI/UHojVgS/Q==
|
||||||
|
|
||||||
caniuse-lite@^1.0.30001426:
|
|
||||||
version "1.0.30001441"
|
|
||||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001441.tgz#987437b266260b640a23cd18fbddb509d7f69f3e"
|
|
||||||
integrity sha512-OyxRR4Vof59I3yGWXws6i908EtGbMzVUi3ganaZQHmydk1iwDhRnvaPG2WaR0KcqrDFKrxVZHULT396LEPhXfg==
|
|
||||||
|
|
||||||
ccount@^2.0.0:
|
ccount@^2.0.0:
|
||||||
version "2.0.1"
|
version "2.0.1"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue