Merge remote-tracking branch 'origin/main'
All checks were successful
Rebuild signaller for deprived.dev to rebuild site / test_service (push) Successful in 22s
23
.forgejo/workflows/signal-rebuild.yml
Normal file
|
@ -0,0 +1,23 @@
|
|||
name: Rebuild signaller for deprived.dev to rebuild site
|
||||
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
test_service:
|
||||
runs-on: native
|
||||
|
||||
steps:
|
||||
- name: Get branch
|
||||
run: echo "BRANCH=$(echo "${{ gitea.ref }}" | cut -d'/' -f3)" >> $GITHUB_ENV
|
||||
- name: Signal deprived.dev for rebuild
|
||||
env:
|
||||
SSH_PRIVATE_KEY: ${{ secrets.SSH_KEY }}
|
||||
run: |
|
||||
sshkey=$(mktemp)
|
||||
trap "rm -rf $sshkey" exit
|
||||
echo -e $SSH_PRIVATE_KEY > $sshkey
|
||||
service="build-deprived-website-$BRANCH"
|
||||
sshargs="-o LogLevel=ERROR -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"
|
||||
echo "Starting systemd oneshot service: $service"
|
||||
ssh -i $sshkey -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no deprivedbuilder@deprived.dev -t "sudo /run/current-system/sw/bin/systemctl start $service"
|
||||
echo "Build Log: $(ssh -i $sshkey $sshargs deprivedbuilder@deprived.dev "cat ~/latest_build.log")"
|
0
build.sh
Executable file → Normal file
|
@ -10,7 +10,12 @@
|
|||
"sourceMap": true,
|
||||
"strict": true,
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true
|
||||
"allowImportingTsExtensions": true,
|
||||
"paths": {
|
||||
"@images/*": ["./src/images/*"],
|
||||
"@src/*": ["./src/*"],
|
||||
"@static/*": ["./static/*"]
|
||||
}
|
||||
}
|
||||
// Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias and https://kit.svelte.dev/docs/configuration#files
|
||||
//
|
||||
|
|
BIN
src/lib/zhen/cv-comps/VRNerd.jpg
Normal file
After Width: | Height: | Size: 673 KiB |
BIN
src/lib/zhen/cv-comps/YaaummaLogo.png
Normal file
After Width: | Height: | Size: 14 KiB |
64
src/routes/baller/+page.svelte
Normal file
|
@ -0,0 +1,64 @@
|
|||
<script lang="ts">
|
||||
import { onMount } from "svelte";
|
||||
|
||||
let scrollY = 0;
|
||||
|
||||
const animateInterval = 16;
|
||||
const startMove = 64;
|
||||
const numFrames = 312;
|
||||
|
||||
let framesLoaded = 1;
|
||||
let ballsLoaded = 0;
|
||||
let frameLoader: HTMLImageElement;
|
||||
let frameLoader2: HTMLImageElement;
|
||||
let frameLoader3: HTMLImageElement;
|
||||
let frameLoader4: HTMLImageElement;
|
||||
|
||||
onMount(()=>{
|
||||
|
||||
// Force load once
|
||||
ballsLoaded = 4;
|
||||
onLoaded();
|
||||
});
|
||||
|
||||
function onLoaded (){
|
||||
ballsLoaded++;
|
||||
if (ballsLoaded > 3) {
|
||||
frameLoader.src = "/images/spinning_cat/untitled_" + framesLoaded.toString().padStart(5, '0') + ".png";
|
||||
framesLoaded++;
|
||||
frameLoader2.src = "/images/spinning_cat/untitled_" + framesLoaded.toString().padStart(5, '0') + ".png";
|
||||
framesLoaded++;
|
||||
frameLoader3.src = "/images/spinning_cat/untitled_" + framesLoaded.toString().padStart(5, '0') + ".png";
|
||||
framesLoaded++;
|
||||
frameLoader4.src = "/images/spinning_cat/untitled_" + framesLoaded.toString().padStart(5, '0') + ".png";
|
||||
framesLoaded++;
|
||||
ballsLoaded = 0;
|
||||
}
|
||||
}
|
||||
|
||||
let frameIndex = 0;
|
||||
$: frameIndex = Math.min(Math.floor(Math.max(scrollY-startMove, 0) / animateInterval+1), numFrames);
|
||||
</script>
|
||||
|
||||
<svelte:window bind:scrollY />
|
||||
|
||||
<div class="w-full flex justify-center" style="height: 5000px;">
|
||||
<!-- Image Loader -->
|
||||
<img on:load={()=>{onLoaded()}} bind:this={frameLoader} style="height: 0.01px; width: 0.01px;" class="" src="/images/spinning_cat/untitled_00001.png" alt="">
|
||||
<img on:load={()=>{onLoaded()}} bind:this={frameLoader2} style="height: 0.01px; width: 0.01px;" class="" src="/images/spinning_cat/untitled_00001.png" alt="">
|
||||
<img on:load={()=>{onLoaded()}} bind:this={frameLoader3} style="height: 0.01px; width: 0.01px;" class="" src="/images/spinning_cat/untitled_00001.png" alt="">
|
||||
<img on:load={()=>{onLoaded()}} bind:this={frameLoader4} style="height: 0.01px; width: 0.01px;" class="" src="/images/spinning_cat/untitled_00001.png" alt="">
|
||||
|
||||
<!-- add "top-0" so it sticks at the top -->
|
||||
<div class="sticky top-0" style="width: 200px; height: 200px;">
|
||||
<div class="flex justify-center items-center" style="width: 200px; height: 200px;">
|
||||
<img src="/images/spinning_cat/untitled_{frameIndex.toString().padStart(5, '0')}.png" class="object-contain w-full h-full" alt="">
|
||||
</div>
|
||||
<div>{frameIndex}</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="h-[1000px] bg-amber-700 w-full">
|
||||
|
||||
</div>
|
258
src/routes/comps/Zooter.svelte
Normal file
|
@ -0,0 +1,258 @@
|
|||
<script lang="ts">
|
||||
import svelteLogo from "$lib/svelteLogos/svelte-logo.png";
|
||||
import { browser } from "$app/environment";
|
||||
import { onDestroy, onMount } from "svelte";
|
||||
import { ArrowBigDown } from "lucide-svelte";
|
||||
import { fly } from "svelte/transition";
|
||||
const buildTime = __BUILD_TIME__;
|
||||
|
||||
let scrollY = 0;
|
||||
const unscrollSpeed = 100;
|
||||
let unscrollScrollDiv: HTMLDivElement;
|
||||
let totalScroll = 0;
|
||||
|
||||
let unscrollInterval: number | undefined = undefined;
|
||||
let lastScrollTime = 0; // Used to have delay before unscrolling
|
||||
let isBeingTouched = false; // Phone support
|
||||
const unscrollDelay = 100;
|
||||
|
||||
let isLeavingAnimating = false;
|
||||
|
||||
// prevent direct scroll
|
||||
let notFirstScroll = false;
|
||||
|
||||
let tranisitionOverlay: HTMLElement;
|
||||
|
||||
// Function with more scroll control, by chatgpt
|
||||
function smoothScrollTo(targetY: number, duration: number = 500): void {
|
||||
const startY: number = window.scrollY;
|
||||
const diff: number = targetY - startY;
|
||||
|
||||
if (duration <= 0) {
|
||||
window.scrollTo(0, targetY);
|
||||
return;
|
||||
}
|
||||
|
||||
let startTime: number | null = null;
|
||||
|
||||
function step(timestamp: number): void {
|
||||
if (startTime === null) startTime = timestamp;
|
||||
const time = timestamp - startTime;
|
||||
const percent = Math.min(time / duration, 1);
|
||||
|
||||
// easeInOutQuad easing
|
||||
const ease =
|
||||
percent < 0.5
|
||||
? 2 * percent * percent
|
||||
: -1 + (4 - 2 * percent) * percent;
|
||||
|
||||
window.scrollTo(0, startY + diff * ease);
|
||||
|
||||
if (time < duration) {
|
||||
requestAnimationFrame(step);
|
||||
}
|
||||
}
|
||||
|
||||
requestAnimationFrame(step);
|
||||
}
|
||||
|
||||
// Out animation:
|
||||
function flyInFromTop(
|
||||
node: HTMLElement,
|
||||
{
|
||||
y = -window.innerHeight,
|
||||
duration = 400,
|
||||
opacity = 0,
|
||||
}: { y?: number; duration?: number; opacity?: number } = {},
|
||||
) {
|
||||
const {
|
||||
delay = 0,
|
||||
duration: d,
|
||||
easing = (t) => t,
|
||||
css,
|
||||
} = fly(node, { y, duration: d, opacity });
|
||||
|
||||
let start: number;
|
||||
function step(now: number) {
|
||||
if (!start) start = now;
|
||||
const elapsed = now - start - delay;
|
||||
if (elapsed < 0) return requestAnimationFrame(step);
|
||||
const t = Math.min(elapsed / d, 1);
|
||||
if (css) node.style.cssText = css(easing(t), 1 - easing(t));
|
||||
if (elapsed < d) requestAnimationFrame(step);
|
||||
}
|
||||
requestAnimationFrame(step);
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
return; // Fuck this, I'm too lazy to fix this atm
|
||||
totalScroll = document.documentElement.scrollHeight - window.innerHeight;
|
||||
|
||||
unscrollInterval = setInterval(() => {
|
||||
// Prevents scroll to bottom on first try
|
||||
if (
|
||||
!notFirstScroll &&
|
||||
scrollY > totalScroll - unscrollScrollDiv.scrollHeight
|
||||
) {
|
||||
smoothScrollTo(totalScroll - unscrollScrollDiv.scrollHeight * 1.1, 0);
|
||||
|
||||
// Allow further scroll after delay
|
||||
setTimeout(() => {
|
||||
notFirstScroll = true;
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
// console.log("Time with delay: " + (Date.now() - -unscrollDelay) + " Other " + lastScrollTime);
|
||||
// console.log(isBeingTouched);
|
||||
if (
|
||||
!notFirstScroll ||
|
||||
isBeingTouched ||
|
||||
Date.now() < lastScrollTime - -unscrollDelay
|
||||
) {
|
||||
// console.log("nah");
|
||||
return;
|
||||
}
|
||||
|
||||
if (totalScroll - unscrollScrollDiv.scrollHeight < scrollY) {
|
||||
smoothScrollTo(totalScroll - unscrollScrollDiv.scrollHeight, 200);
|
||||
}
|
||||
|
||||
if (totalScroll <= scrollY) {
|
||||
console.log("Hit!");
|
||||
// isLeavingAnimating = true;
|
||||
flyInFromTop(tranisitionOverlay, { duration: 800 });
|
||||
}
|
||||
}, 10);
|
||||
});
|
||||
|
||||
function onScroll() {
|
||||
lastScrollTime = Date.now();
|
||||
// console.log("scroll");
|
||||
}
|
||||
|
||||
function onResize() {
|
||||
totalScroll = document.documentElement.scrollHeight - window.innerHeight;
|
||||
}
|
||||
|
||||
onDestroy(() => {
|
||||
clearInterval(unscrollInterval);
|
||||
});
|
||||
|
||||
export let hideOnPrint: boolean;
|
||||
</script>
|
||||
|
||||
<svelte:window
|
||||
bind:scrollY
|
||||
on:scroll={() => {
|
||||
onScroll();
|
||||
}}
|
||||
on:touchstart={() => {
|
||||
isBeingTouched = true;
|
||||
}}
|
||||
on:touchend={() => {
|
||||
isBeingTouched = false;
|
||||
}}
|
||||
on:resize={onResize}
|
||||
/>
|
||||
|
||||
<div class="{hideOnPrint ? 'hide-on-print' : ''} w-full">
|
||||
<!-- Keep scrolling thing -->
|
||||
<div class="hidden h-64 w-full flex flex-col justify-center items-center">
|
||||
<div>Keep scrolling to veiw Zhen's portfolio site!</div>
|
||||
<div class="flex justify-center">
|
||||
<ArrowBigDown />
|
||||
<ArrowBigDown />
|
||||
<ArrowBigDown />
|
||||
<ArrowBigDown />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="hidden h-64 w-full flex flex-col justify-end items-center"
|
||||
bind:this={unscrollScrollDiv}
|
||||
>
|
||||
<img
|
||||
src="/images/memes/WhatDaDog.png"
|
||||
class="w-32 h-32 object-contain"
|
||||
alt="da dog"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- About footer -->
|
||||
<div
|
||||
class="sticky bottom-0 flex flex-col justify-center pt-8 bg-base-300 mt-8"
|
||||
>
|
||||
<div class="flex justify-center">
|
||||
<div class="grid gap-8 sm:grid-cols-3 items-center w-full">
|
||||
<div class="flex flex-col items-center">
|
||||
<span class="font-bold">© 2023-2025</span>
|
||||
<br />
|
||||
<span>Benjamin Dreyer</span>
|
||||
<br />
|
||||
<span>Oliver Schwenger</span>
|
||||
<br />
|
||||
<span>Sylvester Junge</span>
|
||||
<br />
|
||||
<span>Snorre Ettrup Altschul</span>
|
||||
<br />
|
||||
<span>Zhentao Wei</span>
|
||||
</div>
|
||||
<div class="flex flex-col items-center">
|
||||
<h3><b>About this website</b></h3>
|
||||
<!-- <a href="/" target="_blank">Recursion</a> -->
|
||||
<div class="flex justify-center">
|
||||
This website was made using <a
|
||||
class="grid place-content-center"
|
||||
target="_blank"
|
||||
href="https://kit.svelte.dev/"
|
||||
>
|
||||
<img
|
||||
class="pl-2"
|
||||
src={svelteLogo}
|
||||
style="height: 1.5rem;"
|
||||
alt="SvelteKit logo"
|
||||
/></a
|
||||
>
|
||||
</div>
|
||||
<span
|
||||
>Website <a
|
||||
href="https://git.deprived.dev/DeprivedDevs/deprived-main-website"
|
||||
target="_blank">source code</a
|
||||
></span
|
||||
>
|
||||
</div>
|
||||
<div class="flex flex-col items-center">
|
||||
<h3><b>Contact</b></h3>
|
||||
<a href="mailto:zhen@deprived.dev">zhen@deprived.dev</a>
|
||||
<div class="mt-2"></div>
|
||||
<a
|
||||
href="https://discord.gg/awatEEqc3M"
|
||||
target="_blank"
|
||||
class="social"
|
||||
>
|
||||
<!-- <span>Discord</span> -->
|
||||
<img
|
||||
src="/images/icons/discord.svg"
|
||||
class="w-8 h-8 object-contain"
|
||||
alt="Discord"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="flex w-full justify-center border-t border-base-100 border-dashed"
|
||||
>
|
||||
Last build: {buildTime} (+2 UTC)
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
bind:this={tranisitionOverlay}
|
||||
class="{isLeavingAnimating
|
||||
? ''
|
||||
: 'hidden'} fixed top-0 left-0 w-screen h-screen bg-base-200"
|
||||
></div>
|
||||
<!-- {#if isLeavingAnimating}
|
||||
{/if} -->
|
23
src/routes/zhen/cv/CompsRev3/AlexWatermark.svelte
Normal file
|
@ -0,0 +1,23 @@
|
|||
<script>
|
||||
export let Style = "";
|
||||
</script>
|
||||
|
||||
<div class="container" style="{Style}">
|
||||
ALEX
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.container {
|
||||
position: absolute;
|
||||
display: grid;
|
||||
justify-self: end;
|
||||
vertical-align: bottom;
|
||||
align-self: flex-end;
|
||||
|
||||
// font settings
|
||||
font-size: 80mm;
|
||||
color: #e4e4e4;
|
||||
|
||||
transform: translate(32%, -32%) rotate(-90deg);
|
||||
}
|
||||
</style>
|
27
src/routes/zhen/cv/CompsRev3/BiggestFlex.svelte
Normal file
|
@ -0,0 +1,27 @@
|
|||
<div class="short-profile-container">
|
||||
<div>
|
||||
<b style="text-align:left;">
|
||||
Biggest flex
|
||||
</b>
|
||||
</div>
|
||||
<div>
|
||||
Me and my small group of devs has won each and every gamejam, which we have participated in. <br/>
|
||||
<h1 style="font-size: 0.75rem; color: grey;">*Gamejams that had competitions.</h1>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.short-profile-container{
|
||||
display: grid;
|
||||
place-items: center;
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
.short-profile-container > div:first-child {
|
||||
width: 100%;
|
||||
|
||||
/* Bottom border stripe*/
|
||||
border-bottom: 1mm solid black;
|
||||
}
|
||||
</style>
|
53
src/routes/zhen/cv/CompsRev3/BottomRightDecor.svelte
Normal file
|
@ -0,0 +1,53 @@
|
|||
<script>
|
||||
export let Style = "";
|
||||
</script>
|
||||
|
||||
<div class="container" style={Style}>
|
||||
<div class="w-full flex justify-center">
|
||||
<div class="text-center"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.container {
|
||||
position: absolute;
|
||||
transform: translate(20.3mm, -5mm) rotate(-45deg);
|
||||
display: grid;
|
||||
justify-self: end;
|
||||
vertical-align: bottom;
|
||||
align-self: flex-end;
|
||||
|
||||
z-index: 0;
|
||||
|
||||
> div:nth-child(1) {
|
||||
padding-top: 5mm;
|
||||
//border-bottom: #4472c4 dashed 2mm;
|
||||
|
||||
background-image: linear-gradient(
|
||||
to right,
|
||||
#4472c4 70%,
|
||||
rgba(255, 255, 255, 0) 0%
|
||||
);
|
||||
background-position: top;
|
||||
background-size: 6mm 1.5mm;
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
|
||||
> div:nth-child(2) {
|
||||
background-color: #2f5496;
|
||||
width: 100mm;
|
||||
height: 25mm;
|
||||
|
||||
// Text
|
||||
display: grid;
|
||||
place-content: center;
|
||||
align-content: flex-start;
|
||||
> div {
|
||||
padding-top: 3.5mm;
|
||||
color: #4a7bcf;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
7
src/routes/zhen/cv/CompsRev3/CombinedContacts.svelte
Normal file
|
@ -0,0 +1,7 @@
|
|||
<script>
|
||||
import Contact from "./Contact.svelte";
|
||||
import OtherContact from "./OtherContact.svelte";
|
||||
</script>
|
||||
|
||||
<Contact/>
|
||||
<OtherContact/>
|
76
src/routes/zhen/cv/CompsRev3/Contact.svelte
Normal file
|
@ -0,0 +1,76 @@
|
|||
<div class="container">
|
||||
<div>
|
||||
<b style="text-align:left;">
|
||||
Contact
|
||||
</b>
|
||||
</div>
|
||||
<div class="table-display">
|
||||
<div class="table-item">
|
||||
<div>Email</div>
|
||||
<div>Zhen@deprived.dev</div>
|
||||
</div>
|
||||
<div class="table-item">
|
||||
<div>Phone</div>
|
||||
<div>+45 42535723</div>
|
||||
</div>
|
||||
<div class="table-item">
|
||||
<div>LinkedIn</div>
|
||||
<a href="https://www.linkedin.com/in/zhentao-wei-3a3a0a182/">Zhentao-Wei</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.container{
|
||||
display: grid;
|
||||
place-items: center;
|
||||
width: 70%;
|
||||
}
|
||||
|
||||
.container > div:first-child {
|
||||
width: 100%;
|
||||
|
||||
/* Bottom border stripe*/
|
||||
border-bottom: 1mm solid black;
|
||||
}
|
||||
|
||||
.table-display {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.table-item {
|
||||
display: flex;
|
||||
justify-items: start;
|
||||
|
||||
width: 100%;
|
||||
border-bottom: 0.25mm solid #000000;
|
||||
|
||||
> a {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
> div, > a {
|
||||
color: #000000;
|
||||
|
||||
&:first-child {
|
||||
width: 35%;
|
||||
font-size: 4mm;
|
||||
|
||||
display: grid;
|
||||
place-content: center start;
|
||||
|
||||
border-right: rgba(128, 128, 128, 0.4) dashed 0.1mm;
|
||||
}
|
||||
|
||||
&:nth-child(2) {
|
||||
width: 65%;
|
||||
|
||||
font-size: 3.25mm;
|
||||
display: grid;
|
||||
place-content: center;
|
||||
|
||||
padding-left: 1mm;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
47
src/routes/zhen/cv/CompsRev3/Education.svelte
Normal file
|
@ -0,0 +1,47 @@
|
|||
<script>
|
||||
import placeholder from "$lib/zhen/cv-comps/400x400.png";
|
||||
import DTU_Logo from "$lib/zhen/cv-comps/DTU_Logo.png";
|
||||
import NextLogo from "$lib/zhen/cv-comps/nextKbhLogo.png";
|
||||
import SasLogo from "$lib/zhen/cv-comps/SASLogo.png";
|
||||
import EmphasysLogo from "$lib/zhen/cv-comps/EmphasysLogo.png";
|
||||
|
||||
import IconAndText2 from "./IconAndText2.svelte";
|
||||
</script>
|
||||
|
||||
<div class="container h-10">
|
||||
<div>
|
||||
<b style="text-align:left;"> Education </b>
|
||||
</div>
|
||||
<div class="flex justify-center p-2 ">
|
||||
<IconAndText2 logo={DTU_Logo}>
|
||||
<b>DTU</b><br />
|
||||
<p style="font-size: 0.5rem;">Artificial intelligence</p>
|
||||
</IconAndText2>
|
||||
<IconAndText2 logo={NextLogo}>
|
||||
<b>Next</b><br />
|
||||
<p style="font-size: 0.5rem;">Computer science</p>
|
||||
</IconAndText2>
|
||||
<IconAndText2 logo={SasLogo}>
|
||||
<b>Master class</b><br />
|
||||
<p style="font-size: 0.5rem;">SAS Programming</p>
|
||||
</IconAndText2>
|
||||
<IconAndText2 logo={EmphasysLogo}>
|
||||
<span class="font-semibold">Emphasys center</span><br />
|
||||
<p style="font-size: 0.5rem;">VR development</p>
|
||||
</IconAndText2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.container {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
width: 90%;
|
||||
|
||||
> div:first-child {
|
||||
border-bottom: black 1mm solid;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
78
src/routes/zhen/cv/CompsRev3/Experience.svelte
Normal file
|
@ -0,0 +1,78 @@
|
|||
<script>
|
||||
import placeholder from "$lib/zhen/cv-comps/400x400.png";
|
||||
import MakerspaceLogo from "$lib/zhen/cv-comps/MakerspaceLogo.png";
|
||||
import EposLogo from "$lib/zhen/cv-comps/EposLogo.png";
|
||||
import KhoraLogo from "$lib/zhen/cv-comps/KhoraLogo.jpg";
|
||||
import GrazperAILogo from "$lib/zhen/cv-comps/GrazperLogo.jpg";
|
||||
import YaaummaLogo from "$lib/zhen/cv-comps/YaaummaLogo.png";
|
||||
|
||||
import IconAndText from "./IconAndText.svelte";
|
||||
</script>
|
||||
|
||||
<div class="container">
|
||||
<div>
|
||||
<b style="text-align:left;"> Experience </b>
|
||||
</div>
|
||||
<div class="table">
|
||||
<div class="table-item">
|
||||
<IconAndText logo={YaaummaLogo}>
|
||||
<b>Full-stack</b><br />
|
||||
Yaaumma<br />
|
||||
<i>Feb 2025 - Now</i>
|
||||
</IconAndText>
|
||||
</div>
|
||||
<div class="table-item">
|
||||
<IconAndText logo={GrazperAILogo}>
|
||||
<b>Data annotator</b><br />
|
||||
GrazperAI<br />
|
||||
<i>Jul 2024 - Now</i>
|
||||
</IconAndText>
|
||||
</div>
|
||||
<div class="table-item">
|
||||
<IconAndText logo={MakerspaceLogo}>
|
||||
<b>3D printer manager</b> - Volunteer<br />
|
||||
Makerspace - kildevæld Kulturcenter<br />
|
||||
<i>Nov 2023 - Now</i>
|
||||
</IconAndText>
|
||||
</div>
|
||||
<div class="table-item">
|
||||
<IconAndText logo={EposLogo}>
|
||||
<b>Machine Learning Engineer</b> - Short term intern<br />
|
||||
Product design department - Epos<br />
|
||||
<i>Apr 2024 - Apr 2024</i>
|
||||
</IconAndText>
|
||||
</div>
|
||||
<div class="table-item">
|
||||
<IconAndText logo={KhoraLogo}>
|
||||
<b>Assistant</b> - Short term intern<br />
|
||||
Khora Virtual Reality<br />
|
||||
<i>Oct 2020 - Oct 2020</i>
|
||||
</IconAndText>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.container {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
width: 90%;
|
||||
|
||||
overflow: hidden;
|
||||
|
||||
& > div:first-child {
|
||||
width: 100%;
|
||||
|
||||
/* Bottom border stripe*/
|
||||
border-bottom: 1mm solid black;
|
||||
}
|
||||
}
|
||||
|
||||
.table-item {
|
||||
padding: 2mm;
|
||||
|
||||
&:not(:last-child) {
|
||||
border-bottom: 0.25mm solid #000000;
|
||||
}
|
||||
}
|
||||
</style>
|
43
src/routes/zhen/cv/CompsRev3/IconAndText.svelte
Normal file
|
@ -0,0 +1,43 @@
|
|||
<script lang="ts">
|
||||
export let logo: string;
|
||||
export let logoWidths: string = "10%";
|
||||
|
||||
export let fontSize: string = "3mm";
|
||||
export let lineHeight: string = "3.1mm";
|
||||
|
||||
import { onMount } from "svelte";
|
||||
onMount(() => {
|
||||
imageCaption = logo.split(/(\\|\/)/g).pop();
|
||||
});
|
||||
|
||||
let imageCaption: undefined | string; // Not a high piority, you get the file name and thats it
|
||||
</script>
|
||||
|
||||
<div class="container">
|
||||
<img
|
||||
src={logo}
|
||||
class="bg-white w-10 h-10 object-contain rounded shadow"
|
||||
alt={imageCaption}
|
||||
width={logoWidths}
|
||||
/>
|
||||
<div style="line-height: {lineHeight};">
|
||||
<span style="font-size: {fontSize};">
|
||||
<slot />
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.container {
|
||||
display: flex;
|
||||
justify-items: start;
|
||||
|
||||
& > div {
|
||||
padding-left: 3mm;
|
||||
|
||||
text-align: start;
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
|
31
src/routes/zhen/cv/CompsRev3/IconAndText2.svelte
Normal file
|
@ -0,0 +1,31 @@
|
|||
<script lang="ts">
|
||||
export let logo: string;
|
||||
export let logoWidths: string = "35%";
|
||||
|
||||
|
||||
import { onMount } from "svelte";
|
||||
onMount(() => {
|
||||
imageCaption = logo.split(/(\\|\/)/g).pop();
|
||||
});
|
||||
|
||||
let imageCaption: undefined | string; // Not a high piority, you get the file name and thats it
|
||||
</script>
|
||||
|
||||
<div class=" h-full container flex">
|
||||
<div class="flex h-full w-6 items-center overflow-hidden rounded">
|
||||
<img
|
||||
src={logo}
|
||||
class=" w-6 h-6 object-cover shadow"
|
||||
alt={imageCaption}
|
||||
width={logoWidths}
|
||||
/>
|
||||
</div>
|
||||
<div class="px-1 ml-1 h-full text-[0.5rem]" >
|
||||
<span class="inline " >
|
||||
<slot />
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
53
src/routes/zhen/cv/CompsRev3/LeftTopDecor.svelte
Normal file
|
@ -0,0 +1,53 @@
|
|||
<script>
|
||||
import RepeatedSkills from "./RepeatedSkills.svelte";
|
||||
|
||||
export let Style = "";
|
||||
</script>
|
||||
|
||||
<div class="container" style="{Style}">
|
||||
<div>
|
||||
<RepeatedSkills textOverride={["Hello", "你好", "Hej"]} targetTextHeight={4} targetTextWidth={50} applyRotation={false}/>
|
||||
</div>
|
||||
<div/>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.container {
|
||||
position: absolute;
|
||||
transform: translate(-25mm, 0mm) rotate(-45deg);
|
||||
display: grid;
|
||||
justify-self: start;
|
||||
vertical-align: top;
|
||||
align-self: flex-start;
|
||||
|
||||
z-index: 0;
|
||||
|
||||
> div:nth-child(1) {
|
||||
//background-color: #2f559622;
|
||||
width: 100mm;
|
||||
height: 17.5mm;
|
||||
padding-bottom: 1mm;
|
||||
|
||||
// Text inside
|
||||
display: grid;
|
||||
place-content: center;
|
||||
border-bottom: #4472c4 dotted 0.5mm;
|
||||
|
||||
&:first-child {
|
||||
color: #4472c4;
|
||||
font-size: 3mm;
|
||||
//font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
> div:nth-child(2) {
|
||||
padding-top: 4mm;
|
||||
//border-bottom: #4472c4 dashed 2mm;
|
||||
|
||||
background-image: linear-gradient(to right, #4472c4 70%, rgba(255,255,255,0) 0%);
|
||||
background-position: bottom;
|
||||
background-size: 6mm 1.5mm;
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
}
|
||||
</style>
|
51
src/routes/zhen/cv/CompsRev3/LinkToSource.svelte
Normal file
|
@ -0,0 +1,51 @@
|
|||
<script>
|
||||
import svelteLogo from "$lib/svelteLogos/svelte-logo-cutout.svg"
|
||||
</script>
|
||||
|
||||
<div class="container">
|
||||
<div>
|
||||
This CV was made using html, css and <a class="grid place-content-center" href="https://kit.svelte.dev/"><img src={svelteLogo} alt="SvelteKit logo"/></a>
|
||||
Sources:
|
||||
<a href="https://gitea.deprived.dev/Sveskejuice/deprived-main-website/src/branch/dev/src/routes/zhen/cv/rev2/+page.svelte">CV source code</a>
|
||||
and
|
||||
<a href="/zhen/cv/rev2?hideOnPrint=1">My Website</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.container {
|
||||
z-index: 1;
|
||||
padding-left: 2mm;
|
||||
|
||||
font-size: 0.75rem;
|
||||
|
||||
//white-space: nowrap;
|
||||
|
||||
color: #777777;
|
||||
|
||||
* a {
|
||||
color: #3d6ddc;
|
||||
padding-left: 1mm;
|
||||
padding-right: 1mm;
|
||||
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
div:first-child {
|
||||
display: flex;
|
||||
place-content: center;
|
||||
justify-content: start;
|
||||
|
||||
a:nth-child(1) > img {
|
||||
width: 5mm;
|
||||
|
||||
padding-left: 1mm;
|
||||
padding-right: 1mm;
|
||||
}
|
||||
}
|
||||
|
||||
div:nth-child(2){
|
||||
padding-bottom: 2mm;
|
||||
}
|
||||
}
|
||||
</style>
|
24
src/routes/zhen/cv/CompsRev3/LinkedInQR.svelte
Normal file
|
@ -0,0 +1,24 @@
|
|||
<script>
|
||||
import QRCode from "$lib/zhen/cv-comps/LinkedInQrCode.svg?raw"
|
||||
</script>
|
||||
|
||||
<div class="container">
|
||||
<div>LinkedIn</div>
|
||||
<span class="qrcode">{@html QRCode}</span>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
.qrcode {
|
||||
transform: scale(0.9);
|
||||
}
|
||||
|
||||
.container {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
|
||||
& * {
|
||||
font-size: 7.5mm;
|
||||
}
|
||||
}
|
||||
</style>
|
26
src/routes/zhen/cv/CompsRev3/NameAndImage.svelte
Normal file
|
@ -0,0 +1,26 @@
|
|||
<script lang="ts">
|
||||
import NamePlate from "./NamePlate.svelte";
|
||||
import selfie from "$lib/zhen/cv-comps/VRNerd.jpg"
|
||||
import zylveterSus from "$lib/zhen/cv-comps/zylveterSus.png"
|
||||
</script>
|
||||
|
||||
<div class="nameAndImageContainer">
|
||||
<NamePlate/>
|
||||
<div class="mt-4 w-48 h-48 overflow-hidden shadow-xl rounded-lg flex justify-center items-center">
|
||||
<img src={selfie} class="selfie-constraints object-cover" alt="Zhentao Wei"/>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.nameAndImageContainer {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
}
|
||||
|
||||
.selfie-constraints{
|
||||
|
||||
max-width: 100%;
|
||||
|
||||
}
|
||||
</style>
|
21
src/routes/zhen/cv/CompsRev3/NamePlate.svelte
Normal file
|
@ -0,0 +1,21 @@
|
|||
<div class="name-plate-container">
|
||||
<span style="text-align: center;">
|
||||
<b>
|
||||
Zhentao Wei
|
||||
</b><br/>
|
||||
(He/Him)
|
||||
|
||||
</span>
|
||||
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.name-plate-container{
|
||||
display: grid;
|
||||
place-items: center;
|
||||
width: 60%;
|
||||
|
||||
/* Bottom border stripe*/
|
||||
border-bottom: 1mm solid black;
|
||||
}
|
||||
</style>
|
71
src/routes/zhen/cv/CompsRev3/OtherContact.svelte
Normal file
|
@ -0,0 +1,71 @@
|
|||
<div class="container">
|
||||
<div>
|
||||
<b style="text-align:left;">
|
||||
Other
|
||||
</b>
|
||||
</div>
|
||||
<div class="table-display">
|
||||
<div class="table-item">
|
||||
<div>Itch.io</div>
|
||||
<a href="https://github.com/MagicBOTAlex">botalex.itch.io</a>
|
||||
</div>
|
||||
<div class="table-item">
|
||||
<div>Github</div>
|
||||
<a href="https://botalex.itch.io/">@MagicBOTAlex</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.container{
|
||||
display: grid;
|
||||
place-items: center;
|
||||
width: 70%;
|
||||
}
|
||||
|
||||
.container > div:first-child {
|
||||
width: 100%;
|
||||
|
||||
/* Bottom border stripe*/
|
||||
border-bottom: 1mm solid black;
|
||||
}
|
||||
|
||||
.table-display {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.table-item {
|
||||
display: flex;
|
||||
justify-items: start;
|
||||
|
||||
width: 100%;
|
||||
border-bottom: 0.25mm solid #000000;
|
||||
|
||||
> a {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
> div, > a {
|
||||
color: #000000;
|
||||
&:first-child {
|
||||
width: 35%;
|
||||
font-size: 4mm;
|
||||
|
||||
display: grid;
|
||||
place-content: center start;
|
||||
|
||||
border-right: rgba(128, 128, 128, 0.4) dashed 0.1mm;
|
||||
}
|
||||
|
||||
&:nth-child(2) {
|
||||
width: 65%;
|
||||
|
||||
font-size: 3.25mm;
|
||||
display: grid;
|
||||
place-content: center;
|
||||
|
||||
padding-left: 1mm;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
31
src/routes/zhen/cv/CompsRev3/Profile.svelte
Normal file
|
@ -0,0 +1,31 @@
|
|||
<div class="short-profile-container">
|
||||
<div>
|
||||
<b style="text-align:left;">
|
||||
About me
|
||||
</b>
|
||||
</div>
|
||||
<div>
|
||||
I'm a 20-year-old with a deep passion for programming and
|
||||
technology.
|
||||
Most of my knowledge is self-taught from many places on the
|
||||
internet, so university hasn't helped much. I encourage you to browse my LinkedIn, since I
|
||||
periodically post my hobby projects on there, and my skills are
|
||||
described further in depth.
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.short-profile-container{
|
||||
display: grid;
|
||||
place-items: center;
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
.short-profile-container > div:first-child {
|
||||
width: 100%;
|
||||
|
||||
/* Bottom border stripe*/
|
||||
border-bottom: 1mm solid black;
|
||||
}
|
||||
</style>
|
62
src/routes/zhen/cv/CompsRev3/RepeatedSkills.svelte
Normal file
|
@ -0,0 +1,62 @@
|
|||
<script lang="ts">
|
||||
// Width of num chars and height nom of chars
|
||||
export let targetTextWidth: number;
|
||||
export let targetTextHeight: number;
|
||||
|
||||
export let applyRotation: boolean = true;
|
||||
|
||||
export let textOverride: string[] | undefined = undefined;
|
||||
|
||||
// Assign default value if textOverride is undefined
|
||||
let repeatingText : string[] = textOverride ?? [
|
||||
"C++",
|
||||
"C#",
|
||||
"ARDUINO",
|
||||
"PYTHON",
|
||||
"JAVA",
|
||||
"JAVASCRIPT",
|
||||
"TYPESCRIPT",
|
||||
"HTML",
|
||||
"CSS",
|
||||
];
|
||||
|
||||
function getRandomInt(max: number) {
|
||||
return Math.floor(Math.random() * max);
|
||||
}
|
||||
|
||||
function GrabRandomString() {
|
||||
let outString: string = "";
|
||||
while (outString.length < targetTextWidth) {
|
||||
outString +=
|
||||
repeatingText[
|
||||
getRandomInt(repeatingText.length)
|
||||
] + " ";
|
||||
}
|
||||
|
||||
return outString; // At about target size
|
||||
}
|
||||
</script>
|
||||
|
||||
<div {...$$restProps}>
|
||||
{#each { length: targetTextHeight } as _, i}
|
||||
<span class="{applyRotation ? "rotate45" : ""} SkillsText">
|
||||
{GrabRandomString()}
|
||||
</span>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.SkillsText {
|
||||
text-align: start;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
white-space: nowrap;
|
||||
|
||||
width: 2rem;
|
||||
}
|
||||
|
||||
.rotate45 {
|
||||
transform: rotate(-45deg);
|
||||
}
|
||||
</style>
|
28
src/routes/zhen/cv/CompsRev3/ShortProfile.svelte
Normal file
|
@ -0,0 +1,28 @@
|
|||
<div class="short-profile-container">
|
||||
<div>
|
||||
<b style="text-align:left;">
|
||||
Short profile
|
||||
</b>
|
||||
</div>
|
||||
<div>
|
||||
◾ "AI and data" at DTU. <br>
|
||||
◾ Working at <a class="underline" href="https://grazper.com/">GrazperAI</a> <br/>
|
||||
◾ Volunteer at Kildevæld Makerspace.
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.short-profile-container{
|
||||
display: grid;
|
||||
place-items: center;
|
||||
width: 70%;
|
||||
}
|
||||
|
||||
.short-profile-container > div:first-child {
|
||||
width: 100%;
|
||||
|
||||
/* Bottom border stripe*/
|
||||
border-bottom: 1mm solid black;
|
||||
}
|
||||
</style>
|
88
src/routes/zhen/cv/CompsRev3/TableOfProjects.svelte
Normal file
|
@ -0,0 +1,88 @@
|
|||
<div class="container">
|
||||
<div>
|
||||
<b style="text-align:left;">
|
||||
List of big projects
|
||||
</b>
|
||||
</div>
|
||||
<div class="table-display">
|
||||
<div class="table-item">
|
||||
<div>Computer vision</div>
|
||||
<div>Implimented YoloV1 from scratch. (object detection)</div>
|
||||
</div>
|
||||
<div class="table-item">
|
||||
<div>Arduino</div>
|
||||
<div>Built my own claw machine with 2 dimentions of movement</div>
|
||||
</div>
|
||||
<div class="table-item">
|
||||
<div>App dev</div>
|
||||
<div>Made an Doulingo'ish app for learning chinese in 9 days</div>
|
||||
</div>
|
||||
<div class="table-item">
|
||||
<div>Open-source help</div>
|
||||
<div>Contributed in multiple Open-source projects on github</div>
|
||||
</div>
|
||||
<div class="table-item">
|
||||
<div>PCB designing</div>
|
||||
<div>I am currently designing my own circuit board</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex" style="font-size: 0.75rem; color: sgray; align-content: start; width: 100%;">
|
||||
<p>
|
||||
If you want proof or want to know about other projects. Contact me!
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.container{
|
||||
display: grid;
|
||||
place-items: center;
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
.container > div:first-child {
|
||||
width: 100%;
|
||||
|
||||
/* Bottom border stripe*/
|
||||
border-bottom: 1mm solid black;
|
||||
}
|
||||
|
||||
.table-display {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.table-item {
|
||||
display: flex;
|
||||
justify-items: start;
|
||||
|
||||
width: 100%;
|
||||
border-bottom: 0.25mm solid #000000;
|
||||
|
||||
> a {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
> div, > a {
|
||||
color: #000000;
|
||||
|
||||
&:first-child {
|
||||
width: 30%;
|
||||
font-size: 4mm;
|
||||
|
||||
display: grid;
|
||||
place-content: center start;
|
||||
|
||||
border-right: rgba(128, 128, 128, 0.4) dashed 0.1mm;
|
||||
}
|
||||
|
||||
&:nth-child(2) {
|
||||
width: 70%;
|
||||
|
||||
font-size: 3.25mm;
|
||||
display: grid;
|
||||
|
||||
padding-left: 1mm;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
260
src/routes/zhen/cv/rev3/+page.svelte
Normal file
|
@ -0,0 +1,260 @@
|
|||
<script lang="ts">
|
||||
// Left side
|
||||
import NameAndImage from "../CompsRev3/NameAndImage.svelte";
|
||||
import ShortProfile from "../CompsRev3/ShortProfile.svelte";
|
||||
import CombinedContacts from "../CompsRev3/CombinedContacts.svelte";
|
||||
import LinkedInQR from "../CompsRev3/LinkedInQR.svelte";
|
||||
|
||||
// Right side
|
||||
import Profile from "../CompsRev3/Profile.svelte";
|
||||
import Education from "../CompsRev3/Education.svelte";
|
||||
import Experience from "../CompsRev3/Experience.svelte";
|
||||
import BiggestFlex from "../CompsRev3/BiggestFlex.svelte";
|
||||
import TableOfProjects from "../CompsRev3/TableOfProjects.svelte";
|
||||
|
||||
// Decorations
|
||||
import LeftTopDecor from "../CompsRev3/LeftTopDecor.svelte";
|
||||
import BottomRightDecor from "../CompsRev3/BottomRightDecor.svelte";
|
||||
import AlexWatermark from "../CompsRev3/AlexWatermark.svelte";
|
||||
import RepeatedSkills from "../CompsRev3/RepeatedSkills.svelte";
|
||||
|
||||
// Cedit
|
||||
import LinkToSource from "../CompsRev3/LinkToSource.svelte";
|
||||
|
||||
// Discord embed
|
||||
import preveiwImage from "$lib/zhen/cv-comps/EposCvPreveiw.png";
|
||||
|
||||
// Print detection setup
|
||||
import { onMount } from "svelte";
|
||||
onMount(() => {
|
||||
// Check if the query parameter exists in the URL
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const hideOnPrintParam = urlParams.get("hideOnPrint");
|
||||
|
||||
// If the query parameter is not detected, reload the page with the parameter added
|
||||
if (!hideOnPrintParam) {
|
||||
window.location.href = `${window.location.href}?hideOnPrint=1`;
|
||||
}
|
||||
});
|
||||
|
||||
function getFormattedDate(): string {
|
||||
const date = new Date();
|
||||
const day = String(date.getDate()).padStart(2, "0");
|
||||
const month = String(date.getMonth() + 1).padStart(2, "0");
|
||||
const year = date.getFullYear();
|
||||
|
||||
return `${day}-${month}-${year}`;
|
||||
}
|
||||
</script>
|
||||
|
||||
<title>Zhentao Wei's CV {getFormattedDate()}</title>
|
||||
<meta content="Zhentao Wei's CV" property="og:title" />
|
||||
<meta
|
||||
content="This CV is made completely with svelte + html + css + js"
|
||||
property="og:description"
|
||||
/>
|
||||
<meta content={preveiwImage} property="og:image" />
|
||||
<meta content="#bdd6ee" data-react-helmet="true" name="theme-color" />
|
||||
|
||||
<div class="cv-info-container hide-on-print">
|
||||
<div>
|
||||
Under here is my CV rev1 for an application made entirely in HTML and CSS.
|
||||
The page is designed to be saved as PDF. This can be done by pressing <div
|
||||
class="keyboard-key"
|
||||
>
|
||||
P
|
||||
</div>
|
||||
+
|
||||
<div class="keyboard-key">CTRL</div>
|
||||
, then set scaling to 100% and no margins. Lastly, select save to PDF or print.
|
||||
<br />
|
||||
<br />
|
||||
I have to sadly recommend chrome for this process. Firefox somehow messes with
|
||||
the quality of the PDF :(
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="NotoSans cv-container-container include-in-print">
|
||||
<div class="cv-container sections decorations">
|
||||
<div id="left-section">
|
||||
<LeftTopDecor Style="pointer-events: none;" />
|
||||
<BottomRightDecor Style="pointer-events: none;" />
|
||||
<div
|
||||
class="absolute rotate-12 width-[10cm] h-full right-[15cm]"
|
||||
style="background-color: #bdd6ee"
|
||||
></div>
|
||||
<div>
|
||||
<NameAndImage />
|
||||
<ShortProfile />
|
||||
<CombinedContacts />
|
||||
<LinkedInQR />
|
||||
</div>
|
||||
</div>
|
||||
<div id="leftSectionSeperator"></div>
|
||||
<div id="right-section">
|
||||
<AlexWatermark Style="pointer-events: none;" />
|
||||
<div id="TopRightSkillsText">
|
||||
<RepeatedSkills
|
||||
class="Cozette"
|
||||
targetTextHeight={30}
|
||||
targetTextWidth={75}
|
||||
/>
|
||||
</div>
|
||||
<div id="Credit">
|
||||
<LinkToSource />
|
||||
</div>
|
||||
<div>
|
||||
<Profile />
|
||||
<BiggestFlex />
|
||||
<TableOfProjects />
|
||||
<Experience />
|
||||
<Education />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.cv-info-container {
|
||||
height: 40mm;
|
||||
background-color: #2b2a2a;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
.keyboard-key {
|
||||
display: inline;
|
||||
padding-left: 1mm;
|
||||
padding-right: 1mm;
|
||||
|
||||
border-radius: 2mm;
|
||||
|
||||
background-color: #3e3d3d;
|
||||
}
|
||||
|
||||
> div {
|
||||
width: 80%;
|
||||
height: 60%;
|
||||
}
|
||||
}
|
||||
|
||||
.cv-container-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
display: grid;
|
||||
place-items: center;
|
||||
}
|
||||
|
||||
.cv-container-container * {
|
||||
color: black; // Set all text black
|
||||
}
|
||||
|
||||
.cv-container {
|
||||
width: 210mm;
|
||||
height: 297mm;
|
||||
|
||||
background-color: #eeeeee;
|
||||
|
||||
overflow: visible;
|
||||
display: flex;
|
||||
padding: auto;
|
||||
}
|
||||
|
||||
.sections {
|
||||
// Shared between sections
|
||||
> div {
|
||||
display: grid;
|
||||
z-index: 0;
|
||||
|
||||
// Needed to cuttoff the extra decoration
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#left-section {
|
||||
background-color: #bdd6ee;
|
||||
|
||||
> div:last-child {
|
||||
z-index: 1;
|
||||
width: 100%;
|
||||
|
||||
left: 0;
|
||||
|
||||
display: grid;
|
||||
place-items: center;
|
||||
|
||||
padding-top: 30mm;
|
||||
padding-bottom: 30mm;
|
||||
}
|
||||
}
|
||||
|
||||
#right-section {
|
||||
width: calc(100% / 3 * 2);
|
||||
|
||||
> div:last-child {
|
||||
z-index: 1;
|
||||
width: 100%;
|
||||
|
||||
left: 0;
|
||||
|
||||
display: grid;
|
||||
place-items: center;
|
||||
row-gap: 6mm;
|
||||
|
||||
padding-top: 45mm;
|
||||
padding-bottom: 30mm;
|
||||
|
||||
// Disable interactivity for padding
|
||||
// pointer-events: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.decorations {
|
||||
#leftSectionSeperator {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
width: 0%;
|
||||
z-index: 1;
|
||||
overflow: visible;
|
||||
> div {
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
width: 5mm;
|
||||
z-index: 1;
|
||||
background: linear-gradient(90deg, #3636364f, #00000000);
|
||||
}
|
||||
}
|
||||
> div {
|
||||
#TopRightSkillsText {
|
||||
position: absolute;
|
||||
z-index: 0;
|
||||
|
||||
display: grid;
|
||||
place-items: center;
|
||||
vertical-align: top;
|
||||
width: 100%;
|
||||
|
||||
place-content: center;
|
||||
|
||||
padding: 0;
|
||||
height: 50mm;
|
||||
|
||||
mask-image: linear-gradient(180deg, #000 0%, transparent 110%);
|
||||
|
||||
color: rgb(190, 190, 190);
|
||||
font-family: "CozetteVector";
|
||||
font-size: x-large;
|
||||
}
|
||||
|
||||
#Credit {
|
||||
position: absolute;
|
||||
z-index: 0;
|
||||
|
||||
display: flex;
|
||||
align-self: flex-end;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
55
static/images/cropper.py
Normal file
|
@ -0,0 +1,55 @@
|
|||
import os
|
||||
import glob
|
||||
from PIL import Image
|
||||
from concurrent.futures import ProcessPoolExecutor, as_completed
|
||||
|
||||
# Directories
|
||||
input_dir = 'spinning_cat'
|
||||
output_dir = 'spinning_cat_cropped'
|
||||
|
||||
# Ensure output directory exists
|
||||
def ensure_output_dir():
|
||||
if not os.path.exists(output_dir):
|
||||
os.makedirs(output_dir)
|
||||
|
||||
# Process a single image: crop transparent borders and save
|
||||
def process_image(filepath):
|
||||
try:
|
||||
img = Image.open(filepath)
|
||||
if img.mode != 'RGBA':
|
||||
img = img.convert('RGBA')
|
||||
alpha = img.split()[-1]
|
||||
bbox = alpha.getbbox()
|
||||
cropped = img.crop(bbox) if bbox else img
|
||||
filename = os.path.basename(filepath)
|
||||
out_path = os.path.join(output_dir, filename)
|
||||
cropped.save(out_path)
|
||||
return out_path, None
|
||||
except Exception as e:
|
||||
return filepath, e
|
||||
|
||||
# Main execution: parallel processing
|
||||
|
||||
def main():
|
||||
ensure_output_dir()
|
||||
pattern = os.path.join(input_dir, 'untitled_*.png')
|
||||
files = sorted(glob.glob(pattern))
|
||||
if not files:
|
||||
print(f"No files found in '{input_dir}' with pattern 'untitled_*.png'.")
|
||||
return
|
||||
|
||||
with ProcessPoolExecutor() as executor:
|
||||
futures = {executor.submit(process_image, fp): fp for fp in files}
|
||||
for future in as_completed(futures):
|
||||
fp = futures[future]
|
||||
out_path, error = future.result()
|
||||
if error:
|
||||
print(f"Error processing {fp}: {error}")
|
||||
else:
|
||||
print(f"Cropped and saved: {out_path}")
|
||||
|
||||
print("Processing complete.")
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
BIN
static/images/memes/WhatDaDog.png
Normal file
After Width: | Height: | Size: 116 KiB |
BIN
static/images/spinning_cat/untitled_00001.png
Normal file
After Width: | Height: | Size: 396 KiB |
BIN
static/images/spinning_cat/untitled_00002.png
Normal file
After Width: | Height: | Size: 396 KiB |
BIN
static/images/spinning_cat/untitled_00003.png
Normal file
After Width: | Height: | Size: 398 KiB |
BIN
static/images/spinning_cat/untitled_00004.png
Normal file
After Width: | Height: | Size: 397 KiB |
BIN
static/images/spinning_cat/untitled_00005.png
Normal file
After Width: | Height: | Size: 397 KiB |
BIN
static/images/spinning_cat/untitled_00006.png
Normal file
After Width: | Height: | Size: 397 KiB |
BIN
static/images/spinning_cat/untitled_00007.png
Normal file
After Width: | Height: | Size: 399 KiB |
BIN
static/images/spinning_cat/untitled_00008.png
Normal file
After Width: | Height: | Size: 402 KiB |
BIN
static/images/spinning_cat/untitled_00009.png
Normal file
After Width: | Height: | Size: 402 KiB |
BIN
static/images/spinning_cat/untitled_00010.png
Normal file
After Width: | Height: | Size: 402 KiB |
BIN
static/images/spinning_cat/untitled_00011.png
Normal file
After Width: | Height: | Size: 402 KiB |
BIN
static/images/spinning_cat/untitled_00012.png
Normal file
After Width: | Height: | Size: 402 KiB |
BIN
static/images/spinning_cat/untitled_00013.png
Normal file
After Width: | Height: | Size: 403 KiB |
BIN
static/images/spinning_cat/untitled_00014.png
Normal file
After Width: | Height: | Size: 403 KiB |
BIN
static/images/spinning_cat/untitled_00015.png
Normal file
After Width: | Height: | Size: 403 KiB |
BIN
static/images/spinning_cat/untitled_00016.png
Normal file
After Width: | Height: | Size: 403 KiB |
BIN
static/images/spinning_cat/untitled_00017.png
Normal file
After Width: | Height: | Size: 403 KiB |
BIN
static/images/spinning_cat/untitled_00018.png
Normal file
After Width: | Height: | Size: 403 KiB |
BIN
static/images/spinning_cat/untitled_00019.png
Normal file
After Width: | Height: | Size: 403 KiB |
BIN
static/images/spinning_cat/untitled_00020.png
Normal file
After Width: | Height: | Size: 402 KiB |
BIN
static/images/spinning_cat/untitled_00021.png
Normal file
After Width: | Height: | Size: 402 KiB |
BIN
static/images/spinning_cat/untitled_00022.png
Normal file
After Width: | Height: | Size: 402 KiB |
BIN
static/images/spinning_cat/untitled_00023.png
Normal file
After Width: | Height: | Size: 402 KiB |
BIN
static/images/spinning_cat/untitled_00024.png
Normal file
After Width: | Height: | Size: 403 KiB |
BIN
static/images/spinning_cat/untitled_00025.png
Normal file
After Width: | Height: | Size: 402 KiB |
BIN
static/images/spinning_cat/untitled_00026.png
Normal file
After Width: | Height: | Size: 401 KiB |
BIN
static/images/spinning_cat/untitled_00027.png
Normal file
After Width: | Height: | Size: 400 KiB |
BIN
static/images/spinning_cat/untitled_00028.png
Normal file
After Width: | Height: | Size: 401 KiB |
BIN
static/images/spinning_cat/untitled_00029.png
Normal file
After Width: | Height: | Size: 401 KiB |
BIN
static/images/spinning_cat/untitled_00030.png
Normal file
After Width: | Height: | Size: 400 KiB |
BIN
static/images/spinning_cat/untitled_00031.png
Normal file
After Width: | Height: | Size: 400 KiB |
BIN
static/images/spinning_cat/untitled_00032.png
Normal file
After Width: | Height: | Size: 400 KiB |
BIN
static/images/spinning_cat/untitled_00033.png
Normal file
After Width: | Height: | Size: 401 KiB |
BIN
static/images/spinning_cat/untitled_00034.png
Normal file
After Width: | Height: | Size: 401 KiB |
BIN
static/images/spinning_cat/untitled_00035.png
Normal file
After Width: | Height: | Size: 401 KiB |
BIN
static/images/spinning_cat/untitled_00036.png
Normal file
After Width: | Height: | Size: 400 KiB |
BIN
static/images/spinning_cat/untitled_00037.png
Normal file
After Width: | Height: | Size: 403 KiB |
BIN
static/images/spinning_cat/untitled_00038.png
Normal file
After Width: | Height: | Size: 96 KiB |
BIN
static/images/spinning_cat/untitled_00039.png
Normal file
After Width: | Height: | Size: 97 KiB |
BIN
static/images/spinning_cat/untitled_00040.png
Normal file
After Width: | Height: | Size: 108 KiB |
BIN
static/images/spinning_cat/untitled_00041.png
Normal file
After Width: | Height: | Size: 121 KiB |
BIN
static/images/spinning_cat/untitled_00042.png
Normal file
After Width: | Height: | Size: 129 KiB |
BIN
static/images/spinning_cat/untitled_00043.png
Normal file
After Width: | Height: | Size: 115 KiB |
BIN
static/images/spinning_cat/untitled_00044.png
Normal file
After Width: | Height: | Size: 68 KiB |
BIN
static/images/spinning_cat/untitled_00045.png
Normal file
After Width: | Height: | Size: 111 KiB |
BIN
static/images/spinning_cat/untitled_00046.png
Normal file
After Width: | Height: | Size: 116 KiB |
BIN
static/images/spinning_cat/untitled_00047.png
Normal file
After Width: | Height: | Size: 118 KiB |
BIN
static/images/spinning_cat/untitled_00048.png
Normal file
After Width: | Height: | Size: 80 KiB |
BIN
static/images/spinning_cat/untitled_00049.png
Normal file
After Width: | Height: | Size: 74 KiB |
BIN
static/images/spinning_cat/untitled_00050.png
Normal file
After Width: | Height: | Size: 113 KiB |
BIN
static/images/spinning_cat/untitled_00051.png
Normal file
After Width: | Height: | Size: 126 KiB |
BIN
static/images/spinning_cat/untitled_00052.png
Normal file
After Width: | Height: | Size: 92 KiB |
BIN
static/images/spinning_cat/untitled_00053.png
Normal file
After Width: | Height: | Size: 63 KiB |
BIN
static/images/spinning_cat/untitled_00054.png
Normal file
After Width: | Height: | Size: 94 KiB |
BIN
static/images/spinning_cat/untitled_00055.png
Normal file
After Width: | Height: | Size: 136 KiB |
BIN
static/images/spinning_cat/untitled_00056.png
Normal file
After Width: | Height: | Size: 116 KiB |
BIN
static/images/spinning_cat/untitled_00057.png
Normal file
After Width: | Height: | Size: 73 KiB |
BIN
static/images/spinning_cat/untitled_00058.png
Normal file
After Width: | Height: | Size: 84 KiB |
BIN
static/images/spinning_cat/untitled_00059.png
Normal file
After Width: | Height: | Size: 109 KiB |
BIN
static/images/spinning_cat/untitled_00060.png
Normal file
After Width: | Height: | Size: 109 KiB |
BIN
static/images/spinning_cat/untitled_00061.png
Normal file
After Width: | Height: | Size: 90 KiB |
BIN
static/images/spinning_cat/untitled_00062.png
Normal file
After Width: | Height: | Size: 76 KiB |
BIN
static/images/spinning_cat/untitled_00063.png
Normal file
After Width: | Height: | Size: 105 KiB |
BIN
static/images/spinning_cat/untitled_00064.png
Normal file
After Width: | Height: | Size: 124 KiB |
BIN
static/images/spinning_cat/untitled_00065.png
Normal file
After Width: | Height: | Size: 114 KiB |
BIN
static/images/spinning_cat/untitled_00066.png
Normal file
After Width: | Height: | Size: 70 KiB |
BIN
static/images/spinning_cat/untitled_00067.png
Normal file
After Width: | Height: | Size: 93 KiB |
BIN
static/images/spinning_cat/untitled_00068.png
Normal file
After Width: | Height: | Size: 110 KiB |
BIN
static/images/spinning_cat/untitled_00069.png
Normal file
After Width: | Height: | Size: 117 KiB |
BIN
static/images/spinning_cat/untitled_00070.png
Normal file
After Width: | Height: | Size: 78 KiB |