Merge remote-tracking branch 'origin/main'
All checks were successful
Rebuild signaller for deprived.dev to rebuild site / test_service (push) Successful in 22s

This commit is contained in:
MagicBOTAlex 2025-06-14 07:48:44 +02:00
commit b1dfcf4d00
344 changed files with 1531 additions and 16 deletions

View 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
View file

View 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
//

Binary file not shown.

After

Width:  |  Height:  |  Size: 673 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View 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>

View 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} -->

View 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>

View 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>

View 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>

View file

@ -0,0 +1,7 @@
<script>
import Contact from "./Contact.svelte";
import OtherContact from "./OtherContact.svelte";
</script>
<Contact/>
<OtherContact/>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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
View 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()

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 396 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 396 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 398 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 397 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 397 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 397 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 399 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 402 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 402 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 402 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 402 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 402 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 403 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 403 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 403 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 403 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 403 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 403 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 403 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 402 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 402 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 402 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 402 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 403 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 402 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 401 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 400 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 401 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 401 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 400 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 400 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 400 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 401 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 401 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 401 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 400 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 403 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

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