Compare commits

..

No commits in common. "main" and "dev" have entirely different histories.
main ... dev

448 changed files with 7555 additions and 9342 deletions

View file

@ -1,23 +0,0 @@
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")"

View file

View file

@ -1,16 +0,0 @@
#!/usr/bin/env bash
# motherfucking hate npm. Spent 3 hours debugging that when
# NODE_ENV is set to production, npm won't install anything
unset NODE_ENV
git pull
git reset --hard main
npm ci
echo "Ready to build, building..."
npm run build
echo "Done rebuilding!"

View file

@ -10,12 +10,7 @@
"sourceMap": true,
"strict": true,
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"paths": {
"@images/*": ["./src/images/*"],
"@src/*": ["./src/*"],
"@static/*": ["./static/*"]
}
"allowImportingTsExtensions": true
}
// Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias and https://kit.svelte.dev/docs/configuration#files
//

3409
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -6,33 +6,36 @@
"dev": "vite dev",
"build": "vite build",
"preview": "vite preview",
"check": "svelte-kit sync && svelte-check --tsconfig ./jsconfig.json --compiler-warnings \"css-unused-selector:ignore,unused-export-let:ignore\" --threshold error",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./jsconfig.json --watch --threshold error"
"check": "svelte-kit sync && svelte-check --tsconfig ./jsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./jsconfig.json --watch"
},
"devDependencies": {
"@poppanator/sveltekit-svg": "^4.2.1",
"@sveltejs/adapter-auto": "^3.0.0",
"@sveltejs/adapter-static": "^3.0.1",
"@sveltejs/kit": "^2.20.4",
"@sveltejs/kit": "^2.0.0",
"@sveltejs/vite-plugin-svelte": "^3.0.0",
"@tailwindcss/typography": "^0.5.15",
"@zerodevx/svelte-img": "^2.1.0",
"autoprefixer": "^10.4.20",
"daisyui": "^5.0.12",
"daisyui": "^4.12.12",
"postcss": "^8.4.47",
"sass": "^1.77.4",
"svelte": "^5.25.7",
"svelte-check": "^3.8.6",
"svelte": "^4.2.7",
"svelte-check": "^3.6.0",
"svelte-confetti": "^1.0.0",
"svelte-highlight": "^7.6.0",
"tailwindcss": "^4.1.3",
"svelte-parallax": "^0.6.0",
"tailwindcss": "^3.4.13",
"typescript": "^5.0.0",
"vite": "^5.0.3",
"vite-plugin-svgr": "^4.2.0"
},
"type": "module",
"dependencies": {
"@lucide/svelte": "^0.487.0",
"@tailwindcss/vite": "^4.1.3",
"lucide-svelte": "^0.475.0",
"svelte-katex": "^0.1.2",
"svelte-media-queries": "^1.6.2",
"theme-change": "^2.5.0"
"svelte-particles": "^2.12.0",
"theme-change": "^2.5.0",
"tsparticles-slim": "^2.12.0"
}
}

2502
pnpm-lock.yaml generated

File diff suppressed because it is too large Load diff

6
postcss.config.js Normal file
View file

@ -0,0 +1,6 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}

View file

@ -1,222 +0,0 @@
@import "tailwindcss";
@plugin "daisyui";
@plugin "daisyui/theme" {
name: "Deprived";
default: true;
prefersdark: true;
--color-base-100: oklch(14% 0 0);
--color-base-200: oklch(20% 0 0);
--color-base-300: oklch(26% 0 0);
--color-base-content: oklch(97% 0 0);
--color-primary: oklch(72% 0.219 149.579);
--color-primary-content: oklch(98% 0.018 155.826);
--color-secondary: oklch(58% 0.233 277.117);
--color-secondary-content: oklch(96% 0.018 272.314);
--color-accent: oklch(60% 0.25 292.717);
--color-accent-content: oklch(96% 0.016 293.756);
--color-neutral: oklch(20% 0 0);
--color-neutral-content: oklch(98% 0 0);
--color-info: oklch(58% 0.158 241.966);
--color-info-content: oklch(97% 0.013 236.62);
--color-success: oklch(64% 0.2 131.684);
--color-success-content: oklch(98% 0.031 120.757);
--color-warning: oklch(68% 0.162 75.834);
--color-warning-content: oklch(98% 0.026 102.212);
--color-error: oklch(57% 0.245 27.325);
--color-error-content: oklch(97% 0.013 17.38);
--radius-selector: 1rem;
--radius-field: 0.5rem;
--radius-box: 1rem;
--size-selector: 0.25rem;
--size-field: 0.25rem;
--border: 1px;
--depth: 0;
--noise: 0;
}
@plugin "daisyui/theme" {
name: "dark";
color-scheme: "dark";
--color-base-100: oklch(14% 0 0);
--color-base-200: oklch(20% 0 0);
--color-base-300: oklch(26% 0 0);
--color-base-content: oklch(97% 0 0);
--color-primary: oklch(90% 0.182 98.111);
--color-primary-content: oklch(28% 0.066 53.813);
--color-secondary: oklch(84% 0.143 164.978);
--color-secondary-content: oklch(26% 0.051 172.552);
--color-accent: oklch(87% 0.01 258.338);
--color-accent-content: oklch(13% 0.028 261.692);
--color-neutral: oklch(26% 0 0);
--color-neutral-content: oklch(98% 0 0);
--color-info: oklch(71% 0.143 215.221);
--color-info-content: oklch(98% 0.019 200.873);
--color-success: oklch(76% 0.233 130.85);
--color-success-content: oklch(98% 0.031 120.757);
--color-warning: oklch(70% 0.213 47.604);
--color-warning-content: oklch(98% 0.016 73.684);
--color-error: oklch(65% 0.241 354.308);
--color-error-content: oklch(97% 0.014 343.198);
--radius-selector: 0rem;
--radius-field: 0.5rem;
--radius-box: 1rem;
--size-selector: 0.25rem;
--size-field: 0.25rem;
--border: 1px;
--depth: 0;
--noise: 1;
}
@plugin "daisyui/theme" {
name: "pink";
color-scheme: "light";
--color-base-100: oklch(96% 0.015 12.422);
--color-base-200: oklch(94% 0.03 12.58);
--color-base-300: oklch(89% 0.058 10.001);
--color-base-content: oklch(41% 0.159 10.272);
--color-primary: oklch(86% 0.127 207.078);
--color-primary-content: oklch(30% 0.056 229.695);
--color-secondary: oklch(0% 0 0);
--color-secondary-content: oklch(100% 0 0);
--color-accent: oklch(87% 0.169 91.605);
--color-accent-content: oklch(27% 0.077 45.635);
--color-neutral: oklch(51% 0.222 16.935);
--color-neutral-content: oklch(96% 0.015 12.422);
--color-info: oklch(74% 0.16 232.661);
--color-info-content: oklch(29% 0.066 243.157);
--color-success: oklch(77% 0.152 181.912);
--color-success-content: oklch(27% 0.046 192.524);
--color-warning: oklch(75% 0.183 55.934);
--color-warning-content: oklch(26% 0.079 36.259);
--color-error: oklch(70% 0.191 22.216);
--color-error-content: oklch(25% 0.092 26.042);
--radius-selector: 0.25rem;
--radius-field: 0.25rem;
--radius-box: 0.25rem;
--size-selector: 0.25rem;
--size-field: 0.25rem;
--border: 1px;
--depth: 1;
--noise: 0;
}
@plugin "daisyui/theme" {
name: "netherrack";
color-scheme: "dark";
--color-base-100: oklch(25% 0.092 26.042);
--color-base-200: oklch(39% 0.141 25.723);
--color-base-300: oklch(44% 0.177 26.899);
--color-base-content: oklch(93% 0.032 17.717);
--color-primary: oklch(83% 0.128 66.29);
--color-primary-content: oklch(26% 0.079 36.259);
--color-secondary: oklch(82% 0.111 230.318);
--color-secondary-content: oklch(29% 0.066 243.157);
--color-accent: oklch(78% 0.115 274.713);
--color-accent-content: oklch(25% 0.09 281.288);
--color-neutral: oklch(57% 0.245 27.325);
--color-neutral-content: oklch(97% 0.013 17.38);
--color-info: oklch(71% 0.143 215.221);
--color-info-content: oklch(98% 0.019 200.873);
--color-success: oklch(72% 0.219 149.579);
--color-success-content: oklch(98% 0.018 155.826);
--color-warning: oklch(76% 0.188 70.08);
--color-warning-content: oklch(98% 0.022 95.277);
--color-error: oklch(63% 0.237 25.331);
--color-error-content: oklch(97% 0.013 17.38);
--radius-selector: 2rem;
--radius-field: 2rem;
--radius-box: 2rem;
--size-selector: 0.25rem;
--size-field: 0.25rem;
--border: 1px;
--depth: 1;
--noise: 1;
}
@plugin "daisyui/theme" {
name: "green";
color-scheme: "dark";
--color-base-100: oklch(26% 0.065 152.934);
--color-base-200: oklch(39% 0.095 152.535);
--color-base-300: oklch(44% 0.119 151.328);
--color-base-content: oklch(96% 0.044 156.743);
--color-primary: oklch(80% 0.105 251.813);
--color-primary-content: oklch(28% 0.091 267.935);
--color-secondary: oklch(80% 0.105 251.813);
--color-secondary-content: oklch(28% 0.091 267.935);
--color-accent: oklch(89% 0.196 126.665);
--color-accent-content: oklch(27% 0.072 132.109);
--color-neutral: oklch(52% 0.154 150.069);
--color-neutral-content: oklch(98% 0.018 155.826);
--color-info: oklch(78% 0.154 211.53);
--color-info-content: oklch(30% 0.056 229.695);
--color-success: oklch(79% 0.209 151.711);
--color-success-content: oklch(26% 0.065 152.934);
--color-warning: oklch(85% 0.199 91.936);
--color-warning-content: oklch(28% 0.066 53.813);
--color-error: oklch(71% 0.202 349.761);
--color-error-content: oklch(28% 0.109 3.907);
--radius-selector: 0.25rem;
--radius-field: 0.5rem;
--radius-box: 1rem;
--size-selector: 0.25rem;
--size-field: 0.25rem;
--border: 1px;
--depth: 0;
--noise: 1;
}
.bg-grid-100 {
background:
linear-gradient(-90deg, rgba(255, 255, 255, 0.04) 1px, transparent 1px),
linear-gradient(rgba(255, 255, 255, 0.04) 1px, transparent 1px), #f2f2f2;
background-size:
4px 4px,
4px 4px,
80px 80px,
80px 80px,
80px 80px,
80px 80px,
80px 80px,
80px 80px;
background-color: var(--color-base-100);
}
.bg-grid-200 {
background:
linear-gradient(-90deg, rgba(255, 255, 255, 0.04) 1px, transparent 1px),
linear-gradient(rgba(255, 255, 255, 0.04) 1px, transparent 1px), #f2f2f2;
background-size:
4px 4px,
4px 4px,
80px 80px,
80px 80px,
80px 80px,
80px 80px,
80px 80px,
80px 80px;
background-color: var(--color-base-200);
}
.bg-grid-300 {
background:
linear-gradient(-90deg, rgba(255, 255, 255, 0.04) 1px, transparent 1px),
linear-gradient(rgba(255, 255, 255, 0.04) 1px, transparent 1px), #f2f2f2;
background-size:
4px 4px,
4px 4px,
80px 80px,
80px 80px,
80px 80px,
80px 80px,
80px 80px,
80px 80px;
background-color: var(--color-base-300);
}
@media print {
.hide-on-print {
display: none !important;
}
}

View file

@ -1,42 +1,16 @@
<!doctype html>
<html lang="en" data-theme="Deprived" style="overflow-x: hidden;">
<html lang="en" data-theme="Synthwave">
<head>
<meta charset="utf-8" />
<meta name="description" content="
We are the deprived devs, and we are a team of developers specializing in indie game development, full-stack development, and anything tech-related!">
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<!-- Change theme for site here -->
<link rel="stylesheet" href="/stylesheets/main-theme.css" />
<link rel="stylesheet" href="/stylesheets/global.css" />
%sveltekit.head%
<script>
let theme = null;
if (typeof localStorage !== 'undefined') {
theme = localStorage.getItem('theme');
}
window.AvailableThemes = ["green", "netherrack", "dark", "pink"];
if (!theme) {
const randomNumber = Math.floor(Math.random() * 4);
console.log("Slecting: " + AvailableThemes[randomNumber]);
document.documentElement.setAttribute('data-theme', AvailableThemes[randomNumber]);
localStorage.setItem('theme', AvailableThemes[randomNumber]);
}
//else {
// console.log("Slecting: " + theme);
// document.documentElement.setAttribute('data-theme', theme);
//}
</script>
</head>
<body style="display: contents">
<div class="inline relative w-full h-full">
%sveltekit.body%
</div>
</body>
<div style="display: contents">%sveltekit.body%</div>
</html>

View file

@ -1,5 +1,6 @@
<script lang="ts">
import HorizonalStack from "../Utils/HorizonalStack.svelte";
import Img from '@zerodevx/svelte-img'
import VerticalStack from "../Utils/VerticalStack.svelte";
import ZSpacer from "../Utils/ZSpacer.svelte";
import MediaQuery from 'svelte-media-queries';

Binary file not shown.

Before

Width:  |  Height:  |  Size: 711 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 387 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 756 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 193 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 829 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 708 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 129 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 131 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 123 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 135 KiB

3
src/lib/app.css Normal file
View file

@ -0,0 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.2 KiB

File diff suppressed because one or more lines are too long

View file

@ -11,7 +11,9 @@
version="1.1"
viewBox="0 0 28 20"
width="28"
xml:space="preserve"><defs
xml:space="preserve"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs1" /><path
d="M 2,4 H 26 C 27.104,4 28,3.104 28,2 28,0.896 27.104,0 26,0 H 2 C 0.896,0 0,0.896 0,2 0,3.104 0.896,4 2,4 Z M 26,8 H 2 c -1.104,0 -2,0.896 -2,2 0,1.104 0.896,2 2,2 h 24 c 1.104,0 2,-0.896 2,-2 0,-1.104 -0.896,-2 -2,-2 z m 0,8 H 2 c -1.104,0 -2,0.896 -2,2 0,1.104 0.896,2 2,2 h 24 c 1.104,0 2,-0.896 2,-2 0,-1.104 -0.896,-2 -2,-2 z"
id="path1"

View file

@ -1,150 +1,135 @@
<!-- If url contains "hideOnPrint" param, then detect if start printing then hide elements -->
<script lang="ts">
import "../app.css";
import { fly } from "svelte/transition";
import MediaQuery from "svelte-media-queries";
import { Dices } from "@lucide/svelte";
let hideOnPrint: boolean = $state(false);
import "$lib/app.css";
let { children } = $props();
import { fly } from 'svelte/transition';
import MediaQuery from 'svelte-media-queries';
export let hideOnPrint: boolean;
import DeprivedLogo from "$lib/images/DeprivedLogo.svelte";
import HamburgerMenuIcon from "$lib/images/HamburgerMenuIcon.svelte";
const footerCollapseThreshold: string = "1000px";
const headerCollapseThreshold: string = "1000px";
let footerCollapse: boolean;
let isMobile: boolean = $state(false);
const footerCollapseThreshold : string = '1000px';
const headerCollapseThreshold : string = '1000px';
let footerCollapse : boolean;
let headerCollapse : boolean;
let navbarHidden: boolean = $state(true);
let navbarHidden : boolean = true;
function resetNavBar() {
navbarHidden = true;
}
import { afterNavigate } from "$app/navigation";
import { afterNavigate } from '$app/navigation';
afterNavigate(() => {
const params = new URLSearchParams(window.location.search);
hideOnPrint = params.get("hideOnPrint") === "1";
console.log(hideOnPrint);
hideOnPrint = params.get('hideOnPrint') === '1';
//console.log(hideOnPrint);
});
import { onMount } from "svelte";
import Zooter from "./comps/Zooter.svelte";
onMount(async () => {
const lock = document.createElement("meta");
lock.name = "darkreader-lock";
import { onMount } from 'svelte';
import { themeChange } from 'theme-change'
onMount(() => {
const lock = document.createElement('meta');
lock.name = 'darkreader-lock';
document.head.appendChild(lock);
themeChange(false) // false parameter is required for svelte
});
function nextTheme() {
let theme: string | null = null;
if (typeof localStorage !== "undefined") {
theme = localStorage.getItem("theme");
}
const themesArr = (window as any).AvailableThemes;
let nextTheme = "dark";
if (
theme == "null" ||
theme == "undefined" ||
theme == undefined ||
theme == null
) {
} else {
nextTheme = themesArr[(1 - -themesArr.indexOf(theme)) % themesArr.length];
}
console.log("Slecting: " + nextTheme);
document.documentElement.setAttribute("data-theme", nextTheme);
localStorage.setItem("theme", nextTheme);
}
</script>
{#snippet SwitchThemeButton()}
<div
class="tooltip tooltip-bottom grid place-content-center"
data-tip="Switch theme"
>
<button class="cursor-pointer" onclick={nextTheme}> <Dices /></button>
</div>
{/snippet}
<!-- Detect mobile -->
<MediaQuery
query="(max-width: {footerCollapseThreshold})"
bind:matches={footerCollapse}
/>
<MediaQuery
query="(max-width: {headerCollapseThreshold})"
bind:matches={isMobile}
/>
<MediaQuery query='(max-width: {footerCollapseThreshold})' bind:matches={footerCollapse} />
<MediaQuery query='(max-width: {headerCollapseThreshold})' bind:matches={headerCollapse} />
<!-- Nav bar -->
<div class=" bg-base-200 p-0">
<header class="{hideOnPrint ? 'hide-on-print' : ''} bg-base-300">
<div class="nav-bar pr-4">
{#if !isMobile}
<div class="bg-base-200 p-0">
<header class="{hideOnPrint ? 'hide-on-print' : ''}">
<div class="nav-bar pr-4 bg-base-200">
{#if !headerCollapse}
<div class="desktop">
<a href="/" class="nav-head">
<DeprivedLogo
Class="fill-base-content p-2"
Style="width: 3.5rem; height: auto;"
/>
<!-- <h3 id="logo-text">The Deprived Devs</h3> -->
<DeprivedLogo Class="fill-base-content p-2" Style="width: 3.5rem; height: auto;"/>
<h3 id="logo-text">The Deprived Devs</h3>
</a>
<div class="nav-spacer" />
<select class="prose" data-choose-theme>
<option value="light">Light</option>
<option value="dark">Dark</option>
<option value="synthwave">Synthwave</option>
<option value="retro">Retro</option>
<option value="cyberpunk">Cyberpunk</option>
<option value="valentine">Valentine</option>
<option value="halloween">Halloween</option>
<option value="forest">Forest</option>
<option value="aqua">Aqua</option>
<option value="black">Black</option>
<option value="luxury">Luxury</option>
<option value="dracula">Dracula</option>
<option value="business">Business</option>
<option value="night">Night</option>
<option value="coffee">Coffee</option>
<option value="dim">Dim</option>
<option value="sunset" selected>Sunset</option>
</select>
<!-- <a href="/">Home</a> -->
<!-- <a href="/zhen/notes/physics/1?hideOnPrint=1" target="_blank" style="width: 7.5rem;">Notes</a> -->
{@render SwitchThemeButton()}
<a
href="/zhen/cv/rev2?hideOnPrint=1"
target="_blank"
style="width: 7.5rem;">Zhen CV</a
>
<a href="/tools" style="width: 7.5rem;">Tools</a>
<a href="https://botalex.itch.io/" target="_blank">Games</a>
<a href="/">Home</a>
<a href="https://botalex.itch.io/">Games</a>
<!-- <a href="/posts">Blog</a>
<a href="/about">About</a> -->
</div>
{:else}
<div class="collapsed shadow-xl">
<a onclick={resetNavBar} href="/" class="nav-head">
<DeprivedLogo
Class="fill-base-content p-2"
Style="width: 3.5rem; height: auto;"
/>
<!-- <h3 id="logo-text">The Deprived Devs</h3> -->
<div class="collapsed">
<a on:click={resetNavBar} href="/" class="nav-head">
<DeprivedLogo Class="fill-base-content p-2" Style="width: 3.5rem; height: auto;"/>
<h3 id="logo-text">The Deprived Devs</h3>
</a>
<div class="nav-spacer" />
{@render SwitchThemeButton()}
<div class="px-1"></div>
<button
id="toggle-nav"
onclick={() => {
navbarHidden = !navbarHidden;
console.log(navbarHidden);
}}
>
<HamburgerMenuIcon Class="fill-base-content" />
<button id="toggle-nav" on:click={() => navbarHidden = !navbarHidden}>
<HamburgerMenuIcon Class="fill-base-content"/>
</button>
</div>
{#if !navbarHidden}
<div class="nav-list" transition:fly={{ y: -25, duration: 350 }}>
<!-- <a onclick={resetNavBar} href="/">Home</a> -->
<a
onclick={resetNavBar}
href="https://botalex.itch.io/"
target="_blank">Games</a
>
<a href="/zhen/cv/rev2?hideOnPrint=1" target="_blank">Zhen's CV</a>
<!-- <a onclick={resetNavBar} href="/posts">Blog</a>
<a onclick={resetNavBar} href="/about">About</a> -->
<select data-choose-theme>
<option value="light">Default</option>
<option value="dark">Dark</option>
<option value="cupcake">Cupcake</option>
<option value="bumblebee">Bumblebee</option>
<option value="emerald">Emerald</option>
<option value="corporate">Corporate</option>
<option value="synthwave">Synthwave</option>
<option value="retro">Retro</option>
<option value="cyberpunk">Cyberpunk</option>
<option value="valentine">Valentine</option>
<option value="halloween">Halloween</option>
<option value="garden">Garden</option>
<option value="forest">Forest</option>
<option value="aqua">Aqua</option>
<option value="lofi">Lofi</option>
<option value="pastel">Pastel</option>
<option value="fantasy">Fantasy</option>
<option value="wireframe">Wireframe</option>
<option value="black">Black</option>
<option value="luxury">Luxury</option>
<option value="dracula">Dracula</option>
<option value="cmyk">CMYK</option>
<option value="autumn">Autumn</option>
<option value="business">Business</option>
<option value="acid">Acid</option>
<option value="lemonade">Lemonade</option>
<option value="night">Night</option>
<option value="coffee">Coffee</option>
<option value="winter">Winter</option>
<option value="dim">Dim</option>
<option value="nord">Nord</option>
<option value="sunset">Sunset</option>
</select>
<a on:click={resetNavBar} href="/">Home</a>
<a on:click={resetNavBar} href="https://botalex.itch.io/">Games</a>
<!-- <a on:click={resetNavBar} href="/posts">Blog</a>
<a on:click={resetNavBar} href="/about">About</a> -->
</div>
{/if}
{/if}
@ -152,25 +137,40 @@
</header>
<!-- Page content -->
{@render children?.()}
<slot />
<Zooter bind:hideOnPrint />
<!-- About footer -->
<footer class="{hideOnPrint ? 'hide-on-print' : ''}">
<div class="about-container">
<div class="credits">
<span>© 2023-2024</span>
<br>
<span>Benjamin Dreyer</span>
<br>
<span>Oliver Schwenger</span>
<br>
<span>Sylvester Junge</span>
<br>
<span>Zhentao Wei</span>
<br>
<br>
<span>Website <a href="https://gitea.deprived.dev/Sveskejuice/deprived-main-website/src/branch/dev" target="_blank">source code</a></span>
</div>
<div>
<h3>About Us</h3>
<a href="/about">About</a>
</div>
<div class="contact">
<h3>Contact</h3>
<a href="https://discord.gg/awatEEqc3M" target="_blank" class="social">
<img src="/images/icons/discord.svg" alt="Discord"/>
<span>Discord</span>
</a>
</div>
</div>
</footer>
</div>
{#if footerCollapse}
<style>
.about-container {
flex-direction: column;
justify-content: center !important;
gap: 25px;
}
</style>
{/if}
{#if isMobile}
<style>
</style>
{/if}
<style lang="scss">
/* Nav bar. */
@ -204,6 +204,7 @@
border: none;
}
.nav-list {
display: flex;
flex-direction: column;
@ -267,10 +268,6 @@
display: flex;
flex-wrap: wrap;
justify-content: space-evenly;
& h3 {
font-size: larger;
}
}
.about-container > div {
@ -310,4 +307,26 @@
a:hover {
filter: brightness(130%);
}
@media print {
.hide-on-print {
display: none;
}
}
</style>
{#if footerCollapse}
<style>
.about-container {
flex-direction: column;
justify-content: center !important;
gap: 25px;
}
</style>
{/if}
{#if headerCollapse}
<style>
</style>
{/if}

View file

@ -1,259 +1,168 @@
<script lang="ts">
import ProfileSpacer from './comps/ProfileSpacer.svelte';
import MediaQuery from 'svelte-media-queries';
import NewsCard from '$lib/posts/NewsCard.svelte';
import ShowcaseNewsCard from '$lib/posts/ShowcaseNewsCard.svelte';
import Button from '$lib/IO/Button.svelte';
import { ButtonType } from '$lib/IO/ButtonType.ts';
import Timeline from '../comps/timeline/timeline.svelte';
import { onMount, tick } from 'svelte'
import { Parallax, ParallaxLayer, StickyLayer } from "svelte-parallax";
import { Confetti } from "svelte-confetti"
import { tick } from 'svelte'
import PreviewDeprivedLogo from "$lib/images/DeprivedLogo-NoBackground.png";
import BackgroundVideo from "$lib/videos/DeprivedDevMontage.gif"
import Carousel from './comps/Carousel.svelte';
import Corrobot1 from "$lib/GamePreviews/Corrobot-rebouce-title.jpg"
import Corrobot2 from "$lib/GamePreviews/Corrobot-rebouce-gameplay1.png"
import Corrobot3 from "$lib/GamePreviews/Corrobot-rebouce-gameplay2.png"
import Blood1 from "$lib/GamePreviews/Blood-title.png"
import Blood2 from "$lib/GamePreviews/Blood-preview1.png"
import Blood3 from "$lib/GamePreviews/Blood-preview2.png"
import Blood4 from "$lib/GamePreviews/Blood-preview3.png"
import Blood5 from "$lib/GamePreviews/Blood-preview4.png"
import Time1 from "$lib/GamePreviews/Time-1.png"
import Time2 from "$lib/GamePreviews/time-2.png"
import Time3 from "$lib/GamePreviews/time-3.png"
import Time4 from "$lib/GamePreviews/time-4.png"
import Time5 from "$lib/GamePreviews/time-5.gif"
import Tags from './comps/Tags.svelte';
import NameAndTag from './comps/NameAndTag.svelte';
import Profile from './comps/Profile.svelte';
import FrontFold from './comps/FrontFold.svelte';
const mobileThreshold : string = '600px'; // was 1000px. zhen testing
let mobile : boolean;
onMount(()=> {
let tabTittleElement = window.document.getElementById("TabTittle");
if (tabTittleElement) // Not null
tabTittleElement.innerHTML = "Deprived devs";
});
import { onMount } from 'svelte';
// Restore scroll position on mount
let showConfetti: boolean = false;
function onProgressConfetti(progress: number){
console.log(progress);
showConfetti = !mobile && progress > 0.225 || mobile && progress > 0.32;
}
</script>
<!-- Detect mobile -->
<MediaQuery query='(max-width: {mobileThreshold})' bind:matches={mobile} />
<title id="TabTittle">We are the DEPRIVED DEVS</title>
<meta content="We are the deprived devs" property="og:title" />
<meta content="We make everything frontend, backend, games, websites, machine learning, whatever. We're just abunch of nerds, and we love it!" property="og:description" />
<meta content={PreviewDeprivedLogo} property="og:image" />
<meta content="#bdd6ee" data-react-helmet="true" name="theme-color" />
<div class="pointer-events-auto" style="position: relative; width: 100%; height: 100%; overflow: hidden;">
<img id="backgroundGif" src="{BackgroundVideo}" alt="Background video"/>
<div class="main-title m-auto cozette" style="position: absolute; top: 0; left: 0; right: 0; bottom: 0; display: flex; justify-content: center; align-items: center; gap: 2rem; color: white; padding: 1rem;">
<h1 style="font-size: {!mobile ? 5 : 3}rem; text-shadow: 0.2rem 0.2rem 1rem rgba(0, 0, 0, 0.9);">
{#if !mobile}
Deprived Devs
{:else}
Deprived
<br/>
<span class="-mt-6 prose" style="font-size: 2rem;">
Devs
</span>
{/if}
</h1>
<Parallax sections={2.5}>
<ParallaxLayer span={0.6} rate={0.2} class="pointer-events-none">
<div class="pointer-events-auto" style="position: relative; width: 100%; height: 100%; overflow: hidden;">
<img src="{BackgroundVideo}" style="width: 100%; height: 100%; object-fit: cover; filter: blur(5px) brightness(0.6);" alt="Background video"/>
<div class="main-title m-auto" style="position: absolute; top: 0; left: 0; right: 0; bottom: 0; display: flex; justify-content: center; align-items: center; gap: 2rem; color: white; padding: 1rem;">
<h1 style="font-size: {!mobile ? 5 : 3}rem; text-shadow: 0.2rem 0.2rem 1rem rgba(0, 0, 0, 0.9); z-index: 100;">{@html !mobile ? "Deprived Devs" : "Deprived<br/>Devs"}</h1>
{#if mobile}
<div style="width: 100px; height: 100px;"></div>
{/if}
</div>
</div>
</div>
</ParallaxLayer>
<ParallaxLayer offset={0.55} rate={0.3} class="bg-base-200 pointer-events-none">
<div class="cozette flex flex-col justify-center items-center w-full md:px-8 pt-4">
<h2 class="text-center" style="font-size: {!mobile ? 3 : 2}rem;">
Developers
</h2>
<div class="">
<div class="grid max-lg:grid-cols-1 sm:grid-cols-2 gap-4 p-4 max-lg:px-0 w-full">
</ParallaxLayer>
<Profile name="Zhen / Alex" tags={["Programmer", "3D artist", "UX Designer"]} isMobile={mobile}>
<span>
<p>Hi, I am Alex/Zhen, {@html !mobile ? "" : "<br/>"} I'm that chinese guy.</p>
<ParallaxLayer onProgress={onProgressConfetti} offset={0.75}>
<div class="prose flex justify-center m-auto">
<h1 class="main-title text-center" style="font-size: {!mobile ? 3 : 2}rem;">
Developers!
</h1>
</div>
</ParallaxLayer>
<!-- Decorative: -->
<StickyLayer offset={(!mobile) ? ({ top: 0.4, bottom: 0.75 }) : ({ top: 0.4, bottom: 0.4 })} class="pointer-events-none">
<div class="prose font-mono pointer-events-auto h-full" style="max-width: 100%;">
<div class="flex justify-center items-center h-full relative">
<div class="flex justify-center" style="position: absolute; top: 30vh; {!mobile ? "max-width: 35vw;" : "width: 100%;"}">
{#if showConfetti}
<div>
<Confetti noGravity cone amount={50} colorArray={["white"]} />
</div>
{/if}
</div>
<div class="grid gap-4" style="{!mobile ? "max-width: 50vw;" : ""} background: linear-gradient(to bottom, transparent 0%, oklch(var(--b2)) 5%)">
<FrontFold Title="Alex / Zhen" Checked={true}>
<p>Hi, I am Alex/Zhen, I'm that chinese guy.</p>
<p>Here's my CV: <a href="/zhen/cv/rev2?hideOnPrint=1" style="color:lightblue;">pdf</a></p>
</span>
</Profile>
<Profile name="Sveske / Benjamin" tags={["Programmer", "Back-end Admin"]} isMobile={mobile}>
<span>
<p><span class="inline line-through">Hi, I use Arch, btw. </span> I use NixOS now</p>
<p><a href="https://www.linkedin.com/in/benjamin-dreyer/" target="_blank" style="color:lightblue;">Linked-in</a></p>
</span>
</Profile>
<Profile isSnorre={true} tags={["Programmer"]} isMobile={mobile}/>
<Profile name="Oliver" tags={["Sound/Story","2D Artist", "Programmer"]} isMobile={mobile}>
<span>
<p>Snorre does not get paid.</p>
<p><a href="https://www.linkedin.com/in/oliver-schwenger-291944278/" target="_blank" style="color:lightblue;">Linked-in</a></p>
</span>
</Profile>
<Profile name="Kim" tags={["Cinemachine", "3D Artist", "Programmer"]} isMobile={mobile}>
<span>
<p>Abla espaniol</p>
<p><a href="https://www.linkedin.com/in/kim-rex-de-dios-408860299/" target="_blank" style="color:lightblue;">Linked-in</a></p>
</span>
</Profile>
<Profile name="Zylvester" tags={["Sound/Story", "2D/3D artist"]} isMobile={mobile}>
<span>
<p>Closeted omega weeb</p>
<p><a href="https://www.linkedin.com/in/sylvester-junge-0b2a73196/" target="_blank" style="color:lightblue;">Linked-in</a>, <a href="https://www.youtube.com/watch?v=xvFZjo5PgG0" style="color:lightblue;">Funny link</a></p>
</span>
</Profile>
</FrontFold>
<FrontFold Title="Sveske / Benjamin">
<p>Hi, I use Arch, btw.</p>
<p>Here's my Linked-in: <a href="https://www.linkedin.com/in/benjamin-dreyer/" style="color:lightblue;">Linked-in</a></p>'
</FrontFold>
<FrontFold Title="Zylvester">
<p>Hi, I am [insert text here]</p>
<p>Here's a joke about recursion: <a href="/" style="color:lightblue;">recursion</a></p>
</FrontFold>
</div>
</div>
</div>
</div>
</StickyLayer>
<div class="py-4"></div>
<div class="grid place-content-center place-items-center pointer-events-auto font-mono">
<!-- <article class="pt-16 prose overflow-hidden {mobile ? "px-8" : ""}">
<h2 class="main-title {!mobile ? "text-center m-auto" : "m-0"}" style="font-size: {!mobile ? 3 : 3}rem; ">About us</h2>
<ParallaxLayer offset={1.5} rate={0.3} class="bg-base-200 pointer-events-none" style="box-shadow: 0 -1rem 10px rgba(0, 0, 0, 0.2);" >
<div class="grid place-content-center place-items-center min-h-screen pointer-events-auto">
<article class="pt-16 prose overflow-hidden font-mono {mobile ? "px-8" : ""}">
<h1 class="main-title {!mobile ? "text-center m-auto" : "m-0"}" style="font-size: {!mobile ? 5 : 3}rem; ">About us</h1>
<p>We are a small group of developers and artists who started out as classmates, united by our passion for all things technology.</p>
</article> -->
<!-- Spacer -->
<!-- <div style="width: 50%;" class="{!mobile ? "py-16" : "py-4"}">
<ProfileSpacer/>
</div> -->
<article class="prose {mobile ? "px-8" : ""}">
<h2 class="main-title {!mobile ? "text-center m-auto" : "m-0"}" style="font-size: {!mobile ? 3 : 3}rem; ">Games</h2>
<p>Here are some of our games from various gamejams from the past. <br/>(<span class="font-bold">ONLY</span> 48 hours per game)</p>
</article>
<!-- Spacer -->
<div style="width: 50%;" class="{!mobile ? "py-8" : "py-4"}">
</div>
<div class="{!mobile ? "py-16" : "py-4"}"/>
<div class="grid grid-flow-row gap-4 sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
<!-- Corro rebounce -->
<div class="games card bg-base-100 shadow-xl">
<figure style="height: 15em;">
<Carousel images={[
Corrobot1,
Corrobot2,
Corrobot3,
]}/>
</figure>
<div class="card-body">
<h2 class="card-title">Corrobot-rebounce</h2>
<p>A 3D sequel to Corrobot-Takeover</p>
<br/>
<p>This was made during <a href="https://itch.io/jam/nordic-game-jam-2024/rate/2659665" class="underline">Nordic gamejam 2024</a></p>
<div class="card-actions justify-end">
<a href="https://botalex.itch.io/corrobot-rebounce" target="_blank" class="btn btn-primary text-primary-content">View on itch.io</a>
</div>
</div>
</div>
<!-- Blood -->
<div class="games card bg-base-100 w-96 shadow-xl">
<figure style="height: 15em;">
<Carousel images={[
Blood1,
Blood2,
Blood3,
Blood4,
Blood5,
]}/>
</figure>
<div class="card-body">
<h2 class="card-title">Unnamed blood game</h2>
<p>A game based on an unique kind of combat</p>
<br/>
<p>This was made during <a href="https://itch.io/jam/future-game-makers-jam-2024" class="underline">Future Game Makers</a>, and of course our team won the competition.</p>
<div class="card-actions justify-end">
<a href="https://botalex.itch.io/mop-of-the-dead" target="_blank" class="btn btn-primary text-primary-content">View on itch.io</a>
</div>
</div>
</div>
<!-- Time -->
<div class="games card bg-base-100 w-96 shadow-xl">
<figure style="height: 15em;">
<Carousel images={[
Time1,
Time2,
Time3,
Time4,
Time5,
]}/>
</figure>
<div class="card-body">
<h2 class="card-title">One More Time</h2>
<p>What if time was money? A rougelike where you need to kill for time, which you can choose to spend.</p>
<br/>
<p>This was made during <a href="https://itch.io/jam/dmspiljam-november-2021" class="underline">Denmark Masters jam</a>. This jam has youths allover Denmark to compete, and of course our team won the competition again.</p>
<div class="card-actions justify-end">
<a href="https://botalex.itch.io/one-more-time" target="_blank" class="btn btn-primary text-primary-content">View on itch.io</a>
</div>
</div>
</div>
<div class="games card bg-base-100 w-96 shadow-xl">
<figure class="rounded-b-none" style="height: 15em;">
<div class="bg-grid-100 flex w-full h-full"></div>
</figure>
<div class="card-body">
<h2 class="card-title">What's next?</h2>
<div class="skeleton mt-1 h-4 w-28"></div>
<div class="skeleton h-4 w-full"></div>
<div class="skeleton h-4 w-full"></div>
<div class="flex w-52 flex-col gap-4">
<div class="skeleton h-32 w-full"></div>
<div class="skeleton h-4 w-28"></div>
<div class="skeleton h-4 w-full"></div>
<div class="flex grow"/>
<div class="card-actions justify-end">
<a href="/" target="_blank" class="btn btn-primary text-primary-content text-primary-content">RECURSION!</a>
<div class="skeleton h-4 w-full"></div>
</div>
<div class="flex w-52 flex-col gap-4">
<div class="skeleton h-32 w-full"></div>
<div class="skeleton h-4 w-28"></div>
<div class="skeleton h-4 w-full"></div>
<div class="skeleton h-4 w-full"></div>
</div>
{#if !mobile}
<div class="flex w-52 flex-col gap-4">
<div class="skeleton h-32 w-full"></div>
<div class="skeleton h-4 w-28"></div>
<div class="skeleton h-4 w-full"></div>
<div class="skeleton h-4 w-full"></div>
</div>
<div class="flex w-52 flex-col gap-4">
<div class="skeleton h-32 w-full"></div>
<div class="skeleton h-4 w-28"></div>
<div class="skeleton h-4 w-full"></div>
<div class="skeleton h-4 w-full"></div>
</div>
{/if}
</div>
</div>
<h6 class="prose text-base-300"><b>(These will never load)</b></h6>
</div>
</ParallaxLayer>
<!-- <StickyLayer offset={{ top: 1.75, bottom: 2 }} >
<div class="flex justify-center prose main-title overflow-hidden">
<h1 style="font-size: {!mobile ? 5 : 3}rem;">About us</h1>
</div>
</StickyLayer> -->
</Parallax>
<Timeline/>
<style>
#backgroundGif{
width: 100%;
height: 100%;
max-height: 40vh;
object-fit: cover;
filter: blur(5px) brightness(0.6);
.main-title {
width: 80%;
font-family: var(--title-font);
}
</style>
{#if !mobile}
{#if mobile}
<style>
.games {
width: 24rem /* 384px */;
#news-section {
transition-duration: 500ms;
transition-property: width;
width: 90% !important;
}
.news-container {
flex-direction: column !important;
}
</style>
{:else}
<style>
.games {
width: 80%;
display: flex;
justify-self: center;
.dummy {
width: 0% !important;
}
/* #more-posts { */
/* flex-grow: 1 !important; */
/* } */
</style>
{/if}

View file

@ -1,64 +0,0 @@
<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

@ -1,133 +0,0 @@
<script lang="ts">
import { onMount } from "svelte";
export let images: string[] = []; // Expose images as a parameter
let currentIndex: number = 0;
let startX: number | null = null; // Track touch start X position
let deltaX: number = 0; // Track touch delta X
const nextSlide = (): void => {
currentIndex = (currentIndex + 1) % images.length;
};
const prevSlide = (): void => {
currentIndex = (currentIndex - 1 + images.length) % images.length;
};
const handleTouchStart = (event: TouchEvent): void => {
startX = event.touches[0].clientX;
};
const handleTouchMove = (event: TouchEvent): void => {
if (startX !== null) {
deltaX = event.touches[0].clientX - startX;
}
};
const handleTouchEnd = (): void => {
if (startX !== null) {
if (deltaX > 50) {
prevSlide(); // Swipe right
} else if (deltaX < -50) {
nextSlide(); // Swipe left
}
}
// Reset touch variables
startX = null;
deltaX = 0;
};
</script>
<style>
.carousel {
position: relative;
overflow: hidden;
width: 100%;
max-width: 800px;
height: 100%;
margin: auto;
}
.slides {
display: flex;
transition: transform 0.5s ease-in-out;
width: 100%;
}
.slide {
flex: 0 0 100%;
object-fit: cover;
object-position: center;
}
.controls {
position: absolute;
top: 50%;
width: 100%;
display: flex;
justify-content: space-between;
transform: translateY(-50%);
}
.control {
background: rgba(0, 0, 0, 0.5);
border: none;
color: white;
font-size: 1.5rem;
cursor: pointer;
padding: 0.5rem;
}
.indicators {
display: flex;
justify-content: center;
position: absolute;
bottom: 10px;
width: 100%;
}
.indicator {
width: 10px;
height: 10px;
margin: 0 5px;
background: white;
border-radius: 50%;
opacity: 0.5;
cursor: pointer;
}
.indicator.active {
opacity: 1;
}
</style>
<div
class="carousel"
on:touchstart|preventDefault={handleTouchStart}
on:touchmove|preventDefault={handleTouchMove}
on:touchend|preventDefault={handleTouchEnd}
>
<div
class="slides"
style="transform: translateX(-{currentIndex * 100}%);"
>
{#each images as image (image)}
<img class="slide" src={image} alt="Carousel Slide" />
{/each}
</div>
<div class="controls">
<button class="control" on:click={prevSlide}>&lt;</button>
<button class="control" on:click={nextSlide}>&gt;</button>
</div>
<div class="indicators">
{#each images as _, index}
<div
class="indicator {index === currentIndex ? 'active' : ''}"
on:click={() => (currentIndex = index)}
></div>
{/each}
</div>
</div>

View file

@ -1,10 +0,0 @@
<script>
import Tags from "./Tags.svelte";
export let isMobile = false;
export let tags = ["null"];
</script>
{#if isMobile}
<Tags Tags={tags} isMobile={isMobile}/>
{/if}

View file

@ -1,15 +0,0 @@
<script>
import Tags from "./Tags.svelte"; // Should've used better names lol
export let isMobile = false;
export let name = "";
export let tags = ["null"];
</script>
<div class="flex items-center">
<h2 style="font-size: {!isMobile ? 1.5 : 1.5}rem;">{name}</h2>
<div class="flex flex-grow"/>
{#if !isMobile}
<Tags Tags={tags} isMobile={isMobile}/>
{/if}
</div>

View file

@ -1,67 +0,0 @@
<script>
import MobileTags from "./MobileTags.svelte";
import NameAndTag from "./NameAndTag.svelte";
export let isMobile = false;
export let name = "";
export let tags = ["null"];
export let isSnorre = false;
// Shit code but who cares, if it works /shrug
</script>
<div class="bg-grid-100 border-2 border-base-100 pl-1 pr-4 rounded-md cozette max-lg:pb-2">
{#if !isSnorre}
<div class="developersProfile {isSnorre ? "isSnorre" : ""} pl-1 font-mono">
<NameAndTag name={name} tags={tags} isMobile={isMobile}/>
<slot/>
<MobileTags tags={tags} isMobile={isMobile}/>
</div>
{:else}
<div class="w-full pl-1">
<div class="developersProfile absolute snorre pl-4 font-mono pointer-events-none select-none">
<pre style="font-size: {!isMobile ? 1.5 : 1.5}rem;"> </pre>
<span>
<pre> </pre>
<pre> </pre>
</span>
{#if isMobile}
<pre> </pre>
{/if}
</div>
<div class="developersProfile snorre-overlay relative pl-1 font-mono">
<NameAndTag name="Snorre" tags={tags} isMobile={isMobile}/>
<span>
<p>I'm the diversity hire. <span class="border-b" style="border-image: linear-gradient(to right, red, orange, yellow, green, blue, indigo, violet); border-image-slice: 1;">(Gay)</span></p>
<!-- <p><a href="https://www.linkedin.com/in/snorrealtschul/" target="_blank" style="color:lightblue;">My website</a></p> -->
<p><a href="https://spoodythe.one/" target="_blank" style="color:lightblue;">My website</a></p>
</span>
<MobileTags tags={tags} isMobile={isMobile}/>
</div>
</div>
{/if}
</div>
<style>
.developersProfile:not(.snorre):not(.snorre-overlay){
/* background-image: linear-gradient(var(--color-neutral) 33%, rgba(255,255,255,0) 0%); */
/* background-image: linear-gradient(var(--color-neutral) 100%);
background-position: left;
background-size: 0.1rem 0.5rem;
background-repeat: repeat-y; */
}
.snorre {
/* border-left: dashed transparent 0.1rem;
border-image: linear-gradient(to bottom, red, orange, yellow, green, blue, indigo, violet);
border-image-slice: 1; */
}
.snorre-overlay {
/* background-image: linear-gradient(rgba(255,255,255,0) 0%, rgba(255,255,255,0) 40%, var(--color-base-200) 40%); */
/* background-position: left;
background-size: 0.1rem 0.5rem;
background-repeat: repeat-y; */
}
</style>

View file

@ -1,8 +0,0 @@
<div class="profileSpacer"></div>
<style>
.profileSpacer{
height: 1px;
border-bottom: solid oklch(var(--b3));
}
</style>

View file

@ -1,93 +0,0 @@
<script lang="ts">
export let Tags = ["null"];
export let isMobile = false;
// Define an interface for our detailed color object.
interface ColorObject {
color1: string;
color2: string;
rotation: string;
offset: string;
}
// ColorType can be a simple string or a ColorObject.
type ColorType = string | ColorObject;
// Create an interface for the color mapping.
interface ColorsMapping {
[key: string]: ColorType;
}
// Define a class to manage the colors.
class ColorManager {
private colors: ColorsMapping;
constructor() {
this.colors = {
"programmer": "#0CC27F",
"uxdesigner": "#027893",
"3dartist": "#F4881C",
"2dartist": "#F1EAC0",
"2d/3dartist": { color1: "#F1EAC0", color2: "#F4881C", rotation: "-65deg", offset: "71.5%" },
"sound/story": { color1: "#F3EC2A", color2: "#EEC12A", rotation: "-65deg", offset: "50%" },
"sounddesigner": "#F3EC2A",
"storydesigner": "#EEC12A",
"back-endadmin": "#3236a8",
};
}
// Return the color for the given key or a default value.
getColor(key: string): ColorType {
return this.colors[key] || "#ccc";
}
}
// Create an instance of ColorManager.
const colorManager = new ColorManager();
</script>
<div class="flex gap-2" style="font-size: { !isMobile ? '0.875rem' : '2vw' };">
{#each Tags as tag}
{@const key = tag.replaceAll(" ", "").toLowerCase()}
{@const color = colorManager.getColor(key)}
{#if key.indexOf("/") < 0}
<!-- Single Color Badge -->
<div class="badge2" style="background-color: {typeof color === 'string' ? color : '#ccc'};">
<span class="invert">
{tag}
</span>
</div>
{:else}
<!-- Gradient Badge -->
{#if typeof color === 'object' && color !== null}
<div
class="badge2 cozette"
style="background: linear-gradient({color.rotation}, {color.color2} {color.offset}, {color.color1} {color.offset});">
<span class="invert">
{tag}
</span>
</div>
{:else}
<div class="badge2 cozette" style="background-color: #ccc;">
<span class="invert">
{tag}
</span>
</div>
{/if}
{/if}
{/each}
</div>
<style>
.badge2 {
display: inline-flex;
align-items: center;
justify-content: center;
height: 1.25rem; /* 20px */
line-height: 1.25rem; /* 20px */
width: fit-content;
padding-left: 0.563rem; /* 9.008px */
padding-right: 0.563rem; /* 9.008px */
border-radius: var(--rounded-badge, 1.9rem); /* 30.4px */
}
</style>

View file

@ -1,258 +0,0 @@
<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

@ -1,53 +0,0 @@
<script>
import A4 from "../zhen/notes/physics/sharedComps/A4.svelte";
import ToolButton from "./comps/ToolButton.svelte";
import { BatteryMedium } from '@lucide/svelte';
</script>
<div class="flex justify-center pt-10">
<A4
bottomBorder={false}
bgColor={"rounded-lg bg-base-300"}
class="cozette text-base-content h-full"
>
<div class="p-4 flex flex-col h-full">
<h1 class="text-5xl font-bold">Tools (NOT FINISHED. come back in the future.)</h1>
<span class="w-full text-xl"
>These are the tools collected from different places of the
internet</span
>
<!-- Spacing -->
<div class="pt-14"></div>
<div class="p-4 bg-base-200 rounded-lg">
<h2 class="text-2xl font-bolc">MPUs/SoCs</h2>
<span>Whatever acronym you want lol</span>
<div class="grid grid-cols-3 gap-4">
<ToolButton
title="Sleeping battery life"
desc="Calculates the battery life depending on sleep and non-sleep power usage."
btnText="To calculator"
toolIcon={BatteryMedium}
/>
<!-- <ToolButton
title="Sleeping battery life"
desc="Calculates the battery life depending on sleep and non-sleep power usage."
btnText="To calculator"
toolIcon={BatteryMedium}
/>
<ToolButton
title="Sleeping battery life"
desc="Calculates the battery life depending on sleep and non-sleep power usage."
btnText="To calculator"
toolIcon={BatteryMedium}
/> -->
</div>
</div>
<div class="mt-auto align-text-bottom">
I if you have tool suggestions, then either create an issue, create a pull request, or send an email. I probably wont add it though, since this is free work lol.
</div>
</div>
</A4>
</div>

View file

@ -1,183 +0,0 @@
<script lang="ts">
import A4 from "../../zhen/notes/physics/sharedComps/A4.svelte";
import { BatteryLifeCalculator } from "./pageSrc/BatteryCalc";
import { getMCU, type MCU_Type } from "./pageSrc/MCU_defs";
// let mathMachine = new BatteryLifeCalculator();
let useCustom: boolean = false;
let selectedText: string = "";
let selectedMcu: MCU_Type | undefined = undefined;
$: selectedMcu = getMCU(selectedText)
const options = ["esp32-s3", "esp32-s3"];
</script>
<div class="flex justify-center pt-10">
<A4
bottomBorder={false}
bgColor={"rounded-lg bg-base-300"}
class="cozette text-base-content h-full"
>
<div class="p-4 flex flex-col h-full">
<h1 class="text-5xl font-bold">Battery life calculator</h1>
<span class="w-full text-xl">
Calculates the time a battery will last. Too lazy to explain
more.
</span>
<!-- Spacing -->
<div class="pt-14"></div>
<div class="p-4 bg-base-200 rounded-lg">
<h2 class="text-2xl font-bolc">Software</h2>
<div class="grid grid-cols-2 gap-4">
<div class="form-control">
<span class="text-sm text-slate-300 text-opacity-60"
>Duration of code execution</span
>
<div class="join">
<input
type="number"
class="input input-bordered input-sm join-item appearance-none [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none"
/>
<div
class="bg-base-content bg-opacity-60 join-item grid place-content-center"
>
<span class="text-center pl-1 pr-2">sec</span>
</div>
</div>
</div>
<div class="form-control">
<span class="text-sm text-slate-300 text-opacity-60"
>sleep time</span
>
<div class="join">
<input
type="number"
class="input input-bordered input-sm join-item appearance-none [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none"
/>
<div
class="bg-base-content bg-opacity-60 join-item grid place-content-center"
>
<span class="text-center pl-1 pr-2">sec</span>
</div>
</div>
</div>
<div class="col-span-full">
<h2 class=" text-2xl font-bolc">Hardware</h2>
<div class="flex">
<div
class="text-sm text-slate-300 text-opacity-60 text"
>
Use custom values
</div>
<input
bind:checked={useCustom}
class="ml-2 checkbox checkbox-xs my-auto"
type="checkbox"
/>
</div>
</div>
{#if !useCustom}
<div>
<select
bind:value={selectedText}
class="select select-sm select-bordered w-56 max-w-xs"
>
<option disabled value="">Select a text</option>
{#each options as option}
<option value={option}>{option}</option>
{/each}
</select>
{#if selectedMcu != undefined && selectedMcu?.wifi != undefined}
<p class="mt-4 text-lg">
wifi
</p>
{/if}
{#if selectedMcu != undefined && selectedMcu?.wifi != undefined}
<p class="mt-4 text-lg">
ble
</p>
{/if}
</div>
{:else}
<div class="form-control">
<span class="text-sm text-slate-300 text-opacity-60"
>Duration of code execution</span
>
<div class="join">
<input
type="number"
class="input input-bordered input-sm join-item appearance-none [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none"
/>
<div
class="bg-base-content bg-opacity-60 join-item grid place-content-center"
>
<span class="text-center pl-1 pr-2"
>sec</span
>
</div>
</div>
</div>
<div class="form-control">
<span class="text-sm text-slate-300 text-opacity-60"
>sleep time</span
>
<div class="join">
<input
type="number"
class="input input-bordered input-sm join-item appearance-none [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none"
/>
<div
class="bg-base-content bg-opacity-60 join-item grid place-content-center"
>
<span class="text-center pl-1 pr-2"
>sec</span
>
</div>
</div>
</div>
{/if}
</div>
</div>
<div class="mt-auto align-text-bottom text-center">
Source for the calculations is at this
<a
class="text-blue-500 underline"
target="_blank"
href="https://github.com/simonneutert/batterylife-calculator"
>github</a
>
and the
<a
class="text-blue-500 underline"
target="_blank "
href="https://www.of-things.de/battery-life-calculator.php"
>original website.</a
>
I just mearly made additions.
</div>
</div>
</A4>
</div>
<style>
/* Hide the spinner for Chrome, Safari, Edge, Opera */
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
/* Hide the spinner for Firefox */
input[type="number"] {
-moz-appearance: textfield;
}
</style>

View file

@ -1,123 +0,0 @@
export class BatteryLifeCalculator {
timeRunSeconds: number;
timeSleepSeconds: number;
consumptionActiveMilliAmpHours: number;
consumptionSleepMilliAmpHours: number;
powerBatteryTotalMilliAmpHours: number;
powerBatteryBufferBeforeEmptyPercent: number;
constructor(
timeRunSeconds: number,
timeSleepSeconds: number,
consumptionActiveMilliAmpHours: number,
consumptionSleepMilliAmpHours: number,
powerBatteryTotalMilliAmpHours: number,
powerBatteryBufferBeforeEmptyPercent: number = 20
) {
this.timeRunSeconds = timeRunSeconds;
this.timeSleepSeconds = timeSleepSeconds;
this.consumptionActiveMilliAmpHours = consumptionActiveMilliAmpHours;
this.consumptionSleepMilliAmpHours = consumptionSleepMilliAmpHours;
this.powerBatteryTotalMilliAmpHours = powerBatteryTotalMilliAmpHours;
this.powerBatteryBufferBeforeEmptyPercent = powerBatteryBufferBeforeEmptyPercent;
console.log(
"The source of this battery calc is here: https://github.com/simonneutert/batterylife-calculator\nI was too lazy to make the math myself."
);
}
// public API
milliAmpToMicroAmp(milliAmps: number): number {
return milliAmps * 1000;
}
microAmpToMilliAmp(milliAmps: number): number {
return milliAmps * 0.001;
}
calculate(): {
powerAveragePerHour: number;
runtimeHoursEstimated: number;
runtimeDaysEstimated: number;
runtimeDaysRemainingHoursEstimated: number;
} {
return {
powerAveragePerHour: this.powerEstimatedHourly(),
runtimeHoursEstimated: this.runtimeHoursEstimated(),
runtimeDaysEstimated: this.runtimeDaysEstimated(),
runtimeDaysRemainingHoursEstimated: this.runtimeDaysRemainingHoursEstimated(),
};
}
powerEstimatedHourly(): number {
return this.calcPowerEst(
this.powerRun(),
this.consumptionActiveMilliAmpHours,
this.powerSleep(),
this.consumptionSleepMilliAmpHours
);
}
runtimeHoursEstimated(): number {
return parseInt((this.powerLipo() / this.powerEstimatedHourly()).toString(), 10);
}
runtimeDaysEstimated(): number {
return parseInt((this.runtimeHoursEstimated() / 24).toString(), 10);
}
runtimeDaysRemainingHoursEstimated(): number {
return parseInt((this.runtimeHoursEstimated() % 24).toString(), 10);
}
// private methods
private roundOff(x: number): number {
return Math.round(x * 100.0) / 100.0;
}
private calcPowerLipo(x: number, y: number): number {
return parseFloat(((x * (100 - y)) / 100).toString());
}
private calcRuns(x: number, y: number): number {
return parseFloat((60 / (x + y)).toString());
}
private calcRunsHour(x: number, y: number): number {
return parseFloat((3600 / (x + y)).toString());
}
private calcPowerRun(x: number, y: number): number {
return parseFloat(((x / (x + y)) * 3600).toString());
}
private calcPowerSleep(x: number, y: number): number {
return parseFloat(((y / (x + y)) * 3600).toString());
}
powerLipo(): number {
return this.calcPowerLipo(this.powerBatteryTotalMilliAmpHours, this.powerBatteryBufferBeforeEmptyPercent);
}
runs(): number {
return this.calcRuns(this.timeRunSeconds, this.timeSleepSeconds);
}
runsHour(): number {
return this.calcRunsHour(this.timeRunSeconds, this.timeSleepSeconds);
}
powerRun(): number {
return this.calcPowerRun(this.timeRunSeconds, this.timeSleepSeconds);
}
powerSleep(): number {
return this.calcPowerSleep(this.timeRunSeconds, this.timeSleepSeconds);
}
private calcPowerEst(a: number, b: number, c: number, d: number): number {
return parseFloat(((a / 3600) * b + (c / 3600) * d).toString());
}
}

View file

@ -1,63 +0,0 @@
export interface MCU_Type {
name: string;
cpu: { [key: string]: Number }; // state: power consumption
sleep: { [key: string]: Number };
wifi?: { [key: string]: Number };
bluetooth?: { [key: string]: Number };
}
const MCUs: MCU_Type[] = [
{
name: "esp32-s3",
cpu: { // mili amps
single_core_40MHz: 21.8,
dual_core_40MHz: 24.4,
single_core_80MHz: 42.6,
dual_core_80MHz: 47.3,
single_core_160MHz: 54.6,
dual_core_160MHz: 54.1,
single_core_240MHz: 65.9,
dual_core_240MHz: 81.3,
},
sleep: {
},
wifi: { //
dBm_21: 318.2,
sleep: 10
},
bluetooth: {
active: 100,
sleep: 5
}
},
{
name: "esp32-c3",
cpu: {
single_core_80MHz: 22,
single_core_160MHz: 54.6,
},
sleep: {
},
wifi: {
active: 110,
sleep: 9
},
bluetooth: {
active: 90,
sleep: 4
}
}
];
export function getMCU(name: string): MCU_Type | undefined{
for (let i = 0; i < MCUs.length; i++) {
const element = MCUs[i];
if (element.name == name)
return element
}
return undefined;
}

View file

@ -1,27 +0,0 @@
<script lang="ts">
import type { Component } from 'svelte';
export let toolIcon: Component | undefined = undefined;
export let title: string = "Sleeping battery life";
export let desc: string = "Calculates the battery life depending on sleep and non-sleep power usage.";
export let btnText: string = "To calculator";
export let btnHref: string = "/tools/battery-life-calculator";
</script>
<div class="min-w-10 min-h-10 bg-base-100 rounded-lg">
<div class="p-4">
<div class="flex">
<div class="font-bold text-xl">{title}</div>
</div>
<div class="text-sm">{desc}</div>
<div class="flex pt-4">
{#if toolIcon != undefined}
<svelte:component this={toolIcon}/>
{/if}
<a href="{btnHref}" class="btn ml-auto btn-primary btn-sm">{btnText}</a>
</div>
</div>
</div>

View file

@ -0,0 +1,17 @@
<script lang="ts">
import TopAnimatedBackground from "./Comps/TopAnimatedBackground.svelte";
import { Parallax, ParallaxLayer, StickyLayer } from "svelte-parallax";
import ZhenInformatikTimeline from "./Comps/ZhenInformatikTimeline.svelte"
import AboutMe from "./Comps/AboutMe.svelte"
</script>
<Parallax sections={3} config={{ stiffness: 0.1, damping: 0.3 }}>
<TopAnimatedBackground/>
<ParallaxLayer rate={0.5} offset={0.5} style="background-color: var(--background);">
<AboutMe/>
</ParallaxLayer>
<ParallaxLayer rate={0.5} offset={0.8} style="background-color: var(--background);">
<ZhenInformatikTimeline/>
</ParallaxLayer>
</Parallax>

View file

@ -1,6 +1,10 @@
<script lang="ts">
import { onMount } from "svelte";
import { Vector2 } from "../zhen/Utils/Vector2";
import { Vector2 } from "./../Utils/Vector2";
import TopNameTextPlate from "./TopNameTextPlate.svelte";
//import { throttle } from "./../Utils/Throttle";
import { Parallax, ParallaxLayer, StickyLayer } from "svelte-parallax";
// Params
let mouseMoveScale: number = 0.25;
@ -109,8 +113,32 @@
<svelte:window on:mousemove={onMouseMoved} />
<div
class="StartPageAnimated top-0 left-0 w-full h-full"
<ParallaxLayer class="StartPageContainer" rate={0.25} offset={0} span={0}>
<div
class="StartPageAnimated"
bind:this={StartPageAnimated}
style="transform: translate({mouseRelativeScaled.x}px, {mouseRelativeScaled.y}px) translateZ(0) rotate(0.001deg);"
>
{#each { length: 100 } as _, i}
<span class="rotate45 SkillsText">
{GrabRandomString()}
</span>
{/each}
</div>
</ParallaxLayer>
<ParallaxLayer rate={0} offset={0.25} span={0}>
<TopNameTextPlate />
</ParallaxLayer>
<!-- <div class="StartPageContainer">
<div class="TopOverlay">
<TopNameTextPlate/>
</div>
<div
class="StartPageAnimated"
id="StartPageAnimated"
bind:this={StartPageAnimated}
style="transform: translate({mouseRelativeScaled.x}px, {mouseRelativeScaled.y}px) translateZ(0) rotate(0.001deg);"
@ -125,6 +153,10 @@
>
{/each}
</div>
</div> -->
<div id="DummyDiv" class="FirefoxSmoothTranition StartPageContainer TopOverlay" style="display: none !important;" />
<style>
.StartPageContainer {
/* height: 40vh; */
@ -141,10 +173,20 @@
}
.StartPageAnimated {
/* background: url("https://i1.adis.ws/i/canon/future_of_forests_header_16x9_dc14bbe1e35040f79bf566eedaf5c8f7?$hero-header-half-16by9-dt$"); */
background-color: #131313;
position: absolute;
height: 150vh;
width: 150vw;
padding: 0;
transition: transform 1000ms cubic-bezier(0.16, 1.63, 0.01, 0.99);
-moz-transition: none;
left: -25vw;
top: -50vh;
justify-content: center;
vertical-align: middle;
display: flex;
@ -172,6 +214,15 @@
color: rgb(66, 66, 66);
}
.TopOverlay {
position: absolute;
z-index: 1;
width: 100%;
height: 100%;
padding: 0;
}
.rotate45 {
transform: rotate(-45deg); /* Rotate the element by 45 degrees */
}

View file

@ -0,0 +1,21 @@
export function throttle(callback, wait) {
let timeoutId = null;
let lastExecutedTime = 0;
return function (...args) {
const currentTime = Date.now();
const execute = () => {
lastExecutedTime = currentTime;
callback.apply(this, args);
};
if (currentTime - lastExecutedTime >= wait) {
execute();
} else {
clearTimeout(timeoutId);
timeoutId = setTimeout(execute, wait - (currentTime - lastExecutedTime));
}
};
}

View file

@ -7,15 +7,15 @@ export class Vector2 {
this.y = y;
}
Add(vec2: Vector2) {
Add(vec2: Vector2){
return new Vector2(this.x + vec2.x, this.y + vec2.y);
}
Sub(vec2: Vector2) {
Sub(vec2: Vector2){
return new Vector2(this.x - vec2.x, this.y - vec2.y);
}
Scale(mult: number) {
Scale(mult: number){
return new Vector2(this.x * mult, this.y * mult);;
}
}

View file

@ -3,6 +3,7 @@
</script>
<div class="container" style="{Style}">
<div/>
<div>
<div>
Thank you! ❤

View file

@ -53,4 +53,11 @@
width: 100%;
}
}
.imagesContainer {
> img {
border-radius: 5mm;
filter: drop-shadow(1mm 1mm 1mm #0000009d);
}
}
</style>

View file

@ -5,10 +5,12 @@
<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>
</div>
<div>
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>
<a href="https://dev.deprived.dev/zhen/cv/rev1?hideOnPrint=1">My Website</a>
</div>
</div>
@ -17,8 +19,6 @@
z-index: 1;
padding-left: 2mm;
font-size: 0.75rem;
//white-space: nowrap;
color: #777777;

View file

@ -5,7 +5,7 @@
</b>
</div>
<div>
◾ "AI and data" at DTU. <br>
◾ "Artificial intelligence and data" student at DTU.
◾ Working at <a class="underline" href="https://grazper.com/">GrazperAI</a> <br/>
◾ Volunteer at Kildevæld Makerspace.
</div>

View file

@ -19,7 +19,7 @@
</div>
<div class="table-item">
<div>Open-source help</div>
<div>Contributed in multiple Open-source projects on github</div>
<div>Have contributed in multiple Open-source project on github</div>
</div>
<div class="table-item">
<div>PCB designing</div>

View file

@ -74,7 +74,7 @@
<LinkedInQR/>
</div>
</div>
<div id="leftSectionSeperator"></div>
<div id="leftSectionSeperator"><div/></div>
<div id="right-section">
<AlexWatermark/>
<div id="TopRightSkillsText">

View file

@ -74,7 +74,7 @@
<LinkedInQR/>
</div>
</div>
<div id="leftSectionSeperator"></div>
<div id="leftSectionSeperator"><div/></div>
<div id="right-section">
<AlexWatermark/>
<div id="TopRightSkillsText">

View file

@ -1,8 +1,8 @@
<script lang="ts">
// Left side
import NameAndImage from "../CompsRev2/NameAndImage.svelte";
import ShortProfile from "../CompsRev2/ShortProfile.svelte";
import CombinedContacts from "../CompsRev2/CombinedContacts.svelte";
import ShortProfile from "../CompsRev2/ShortProfile.svelte"
import CombinedContacts from "../CompsRev2/CombinedContacts.svelte"
import LinkedInQR from "../CompsRev2/LinkedInQR.svelte";
// Right side
@ -22,14 +22,14 @@
import LinkToSource from "../CompsRev2/LinkToSource.svelte";
// Discord embed
import preveiwImage from "$lib/zhen/cv-comps/EposCvPreveiw.png";
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");
const hideOnPrintParam = urlParams.get('hideOnPrint');
// If the query parameter is not detected, reload the page with the parameter added
if (!hideOnPrintParam) {
@ -39,68 +39,58 @@
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 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="Zhentao Wei's Epos CV" property="og:title" />
<meta content="This CV is made completely with 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 :(
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="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;" />
<LeftTopDecor Style="pointer-events: none;"/>
<BottomRightDecor Style="pointer-events: none;"/>
<div>
<NameAndImage />
<ShortProfile />
<CombinedContacts />
<LinkedInQR />
<NameAndImage/>
<ShortProfile/>
<CombinedContacts/>
<LinkedInQR/>
</div>
</div>
<div id="leftSectionSeperator"></div>
<div id="leftSectionSeperator"><div/></div>
<div id="right-section">
<AlexWatermark Style="pointer-events: none;" />
<AlexWatermark Style="pointer-events: none;"/>
<div id="TopRightSkillsText">
<RepeatedSkills targetTextHeight={30} targetTextWidth={75} />
<RepeatedSkills targetTextHeight={30} targetTextWidth={75}/>
</div>
<div id="Credit">
<LinkToSource />
<LinkToSource/>
</div>
<div>
<Profile />
<BiggestFlex />
<TableOfProjects />
<Experience />
<Education />
<Profile/>
<BiggestFlex/>
<TableOfProjects/>
<Experience/>
<Education/>
</div>
</div>
</div>
@ -130,7 +120,13 @@
}
}
.cv-container-container {
@media print {
.hide-on-print {
display: none;
}
}
.cv-container-container{
width: 100%;
height: 100%;
@ -153,6 +149,11 @@
padding: auto;
}
.include-in-print { &, & * {
-webkit-print-color-adjust:exact !important;
print-color-adjust:exact !important;
}}
.sections {
// Shared between sections
> div {
@ -164,7 +165,7 @@
overflow: hidden;
}
#left-section {
#left-section{
background-color: #bdd6ee;
width: calc(100% / 3 * 1);
@ -182,7 +183,7 @@
}
}
#right-section {
#right-section{
width: calc(100% / 3 * 2);
> div:last-child {
@ -199,13 +200,13 @@
padding-bottom: 30mm;
// Disable interactivity for padding
pointer-events: none;
pointer-events:none;
}
}
}
.decorations {
#leftSectionSeperator {
#leftSectionSeperator{
position: relative;
height: 100%;
width: 0%;
@ -237,7 +238,7 @@
mask-image: linear-gradient(180deg, #000 0%, transparent 110%);
color: rgb(190, 190, 190);
font-family: "CozetteVector";
font-family: 'CozetteVector';
font-size: x-large;
}
@ -251,4 +252,3 @@
}
}
</style>

View file

@ -0,0 +1,117 @@
<script lang="ts">
import RepeatedSkills from "../../cv/Comps/RepeatedSkills.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 LinkedIn banner {getFormattedDate()}</title>
<meta content="Zhentao Wei's LinkedIn banner" property="og:title" />
<meta content="This Linkedin banner is made completely with 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 Linkedin banner. This page has been able to be saved as PDF, and the banner can be extracted as an image fro mthe 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="container include-in-print">
<div>
<RepeatedSkills targetTextHeight={70} targetTextWidth={175}/>
</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%;
}
}
@media print {
.hide-on-print {
display: none;
}
}
.include-in-print { &, & * {
-webkit-print-color-adjust:exact !important;
print-color-adjust:exact !important;
}}
.container {
width: 100%;
height: 100%;
display: grid;
place-items: center;
> div:nth-child(1) {
width: 419.1mm;
height: 104.775mm;
background-color: #383636;
overflow: hidden;
display: grid;
place-content: center;
font-size: 10mm;
font-family: cozetteVector;
color: #d4d4d4;
filter: blur(0); // Force save as image
}
> div:nth-child(2){
height: 0;
}
}
</style>

View file

@ -1,16 +0,0 @@
<script lang="ts">
export let bgColor: string | undefined = undefined;
export let bottomBorder: boolean = true;
</script>
<div style="width: 210mm; height: 297mm;" class="{(bgColor)?bgColor:"bg-white"} overflow-y-auto overflow-x-hidden">
<div class="flex flex-col h-full">
<div {...$$restProps}>
<slot></slot>
</div>
{#if bottomBorder}
<div class="border-b-2 mt-auto mb-0 border-dashed border-slate-600 hide-on-print"></div>
{/if}
</div>
</div>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 9.5 KiB

Before After
Before After

View file

@ -1,55 +0,0 @@
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.

Before

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 396 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 396 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 398 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 397 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 397 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 397 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 399 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 402 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 402 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 402 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 402 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 402 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 403 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 403 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 403 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 403 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 403 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 403 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 403 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 402 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 402 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 402 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 402 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 403 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 402 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 401 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 400 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 401 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 401 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 400 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 400 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 400 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 401 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 401 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 401 KiB

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