Merge pull request 'redacted my info, into my database' (#1) from dev into main
All checks were successful
Rebuild signaller for deprived.dev to rebuild site / test_service (push) Successful in 22s
Reviewed-on: #1
|
Before Width: | Height: | Size: 7.4 KiB After Width: | Height: | Size: 7.4 KiB |
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 6.1 KiB After Width: | Height: | Size: 6.1 KiB |
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 5.6 KiB |
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 6.4 KiB After Width: | Height: | Size: 6.4 KiB |
|
Before Width: | Height: | Size: 5.9 KiB After Width: | Height: | Size: 5.9 KiB |
|
Before Width: | Height: | Size: 7.7 KiB After Width: | Height: | Size: 7.7 KiB |
|
Before Width: | Height: | Size: 673 KiB After Width: | Height: | Size: 673 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.4 KiB |
BIN
src/lib/alex/cv-comps/preview.png
Normal file
|
After Width: | Height: | Size: 505 KiB |
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 139 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 391 KiB |
|
Before Width: | Height: | Size: 587 KiB |
1
src/optimizers/fly.ts
Normal file
|
|
@ -0,0 +1 @@
|
|||
export { fly as default } from "svelte/transition";
|
||||
1
src/optimizers/onDestroy.ts
Normal file
|
|
@ -0,0 +1 @@
|
|||
export { onDestroy as default } from "svelte";
|
||||
1
src/optimizers/onMount.ts
Normal file
|
|
@ -0,0 +1 @@
|
|||
export { onMount as default } from "svelte";
|
||||
1
src/optimizers/sveltekit.ts
Normal file
|
|
@ -0,0 +1 @@
|
|||
export { sveltekit as default } from "@sveltejs/kit/vite";
|
||||
1
src/optimizers/tick.ts
Normal file
|
|
@ -0,0 +1 @@
|
|||
export { tick as default } from "svelte";
|
||||
1
src/optimizers/viteThing.js
Normal file
|
|
@ -0,0 +1 @@
|
|||
export { vitePreprocess as default } from "@sveltejs/vite-plugin-svelte";
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
<script lang="ts">
|
||||
import { onMount } from "svelte";
|
||||
import onMount from "@e/onMount";
|
||||
import ShopItemCard from "./comps/ShopItemCard.svelte";
|
||||
import { api } from "@stores";
|
||||
import { ShopItem } from "@src/ts/api/classes/ShopItem";
|
||||
import ShopItem from "@src/ts/api/classes/ShopItem";
|
||||
|
||||
let allItems: undefined | ShopItem[] = undefined;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { onMount } from "svelte";
|
||||
import onMount from "@e/onMount";
|
||||
|
||||
onMount(async () => {
|
||||
console.log(window.location.href);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script lang="ts">
|
||||
import type { ShopItem } from "@src/ts/api/classes/ShopItem";
|
||||
import { ArrowBigRight } from "lucide-svelte";
|
||||
import type ShopItem from "@src/ts/api/classes/ShopItem";
|
||||
import ArrowBigRight from "lucide-svelte/icons/arrow-big-right";
|
||||
|
||||
export let shopItem: ShopItem;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
<!-- 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 fly from "@e/fly";
|
||||
import MediaQuery from "svelte-media-queries";
|
||||
import { Dices } from "@lucide/svelte";
|
||||
import Dices from "@lucide/svelte/icons/dices";
|
||||
import re from "@ts/Redaction/Redactor";
|
||||
let hideOnPrint: boolean = $state(false);
|
||||
|
||||
let { children } = $props();
|
||||
|
|
@ -22,21 +23,31 @@
|
|||
navbarHidden = true;
|
||||
}
|
||||
|
||||
import { afterNavigate } from "$app/navigation";
|
||||
afterNavigate(() => {
|
||||
function handleUrlParams() {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
hideOnPrint = params.get("hideOnPrint") === "1";
|
||||
// console.log(hideOnPrint);
|
||||
if (!!params.get("key")) {
|
||||
localStorage.setItem("key", params.get("key")!);
|
||||
}
|
||||
}
|
||||
|
||||
import { afterNavigate } from "$app/navigation";
|
||||
afterNavigate(() => {
|
||||
handleUrlParams();
|
||||
});
|
||||
|
||||
import { onMount } from "svelte";
|
||||
import onMount from "@e/onMount";
|
||||
import Zooter from "./comps/Zooter.svelte";
|
||||
import CustomScrollBar from "./comps/CustomScrollBar.svelte";
|
||||
|
||||
onMount(async () => {
|
||||
handleUrlParams();
|
||||
|
||||
const lock = document.createElement("meta");
|
||||
lock.name = "darkreader-lock";
|
||||
document.head.appendChild(lock);
|
||||
|
||||
await re.TryGetUnredacter();
|
||||
});
|
||||
|
||||
function nextTheme() {
|
||||
|
|
@ -104,16 +115,14 @@
|
|||
</a>
|
||||
<div class="nav-spacer" />
|
||||
|
||||
<!-- <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/rev3?hideOnPrint=1"
|
||||
href="/cv?hideOnPrint=1"
|
||||
target="_blank"
|
||||
style="width: 7.5rem;"
|
||||
class="text-center justify-center">Zhen CV</a
|
||||
class="text-center justify-center text-md"
|
||||
>{$re?.nick ?? "Alex"}'s CV</a
|
||||
>
|
||||
<!-- <a href="/tools" style="width: 7.5rem;" class="text-center">Tools</a> -->
|
||||
<a href="https://botalex.itch.io/" target="_blank">Games</a>
|
||||
|
|
@ -150,10 +159,8 @@
|
|||
href="https://botalex.itch.io/"
|
||||
target="_blank">Games</a
|
||||
>
|
||||
<a
|
||||
href="/zhen/cv/rev3?hideOnPrint=1"
|
||||
target="_blank"
|
||||
class="justify-center">Zhen's CV</a
|
||||
<a href="/cv?hideOnPrint=1" target="_blank" class="justify-center"
|
||||
>{$re?.nick ?? "Alex"}'s CV</a
|
||||
>
|
||||
<!-- <a onclick={resetNavBar} href="/posts">Blog</a>
|
||||
<a onclick={resetNavBar} href="/about">About</a> -->
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
<script lang="ts">
|
||||
import ProfileSpacer from "./comps/ProfileSpacer.svelte";
|
||||
// import ProfileSpacer from "./comps/ProfileSpacer.svelte";
|
||||
import MediaQuery from "svelte-media-queries";
|
||||
import { onMount, tick } from "svelte";
|
||||
import onMount from "@e/onMount";
|
||||
// import tick from "@e/tick";
|
||||
|
||||
import PreviewDeprivedLogo from "$lib/images/DeprivedLogo-NoBackground.png";
|
||||
|
||||
|
|
@ -23,12 +24,13 @@
|
|||
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 Tags from "./comps/Tags.svelte";
|
||||
// import NameAndTag from "./comps/NameAndTag.svelte";
|
||||
import Profile from "./comps/Profile.svelte";
|
||||
import DeprivedTrackerSection from "./comps/DeprivedTrackerSection.svelte";
|
||||
import re from "@src/ts/Redaction/Redactor";
|
||||
|
||||
const mobileThreshold: string = "600px"; // was 1000px. zhen testing
|
||||
const mobileThreshold: string = "600px"; // was 1000px.
|
||||
let mobile: boolean;
|
||||
let debug = false;
|
||||
|
||||
|
|
@ -101,19 +103,19 @@
|
|||
class="grid max-lg:grid-cols-1 sm:grid-cols-2 gap-4 p-4 max-lg:px-0 w-full"
|
||||
>
|
||||
<Profile
|
||||
name="Zhen / Alex"
|
||||
name={$re?.nick ? $re?.nick + "/Alex" : "Alex"}
|
||||
tags={["Programmer", "3D artist", "UX Designer"]}
|
||||
isMobile={mobile}
|
||||
>
|
||||
<span>
|
||||
<p>
|
||||
Hi, I am Alex/Zhen, {@html !mobile ? "" : "<br/>"} I'm that chinese
|
||||
guy.
|
||||
Hi, I am {$re?.nick ? $re?.nick + "/Alex" : "Alex"}, {@html !mobile
|
||||
? ""
|
||||
: "<br/>"} I'm that chinese guy.
|
||||
</p>
|
||||
<p>
|
||||
Here's my CV: <a
|
||||
href="/zhen/cv/rev3?hideOnPrint=1"
|
||||
style="color:lightblue;">pdf</a
|
||||
Here's my CV: <a href="/cv?hideOnPrint=1" style="color:lightblue;"
|
||||
>pdf</a
|
||||
>
|
||||
</p>
|
||||
</span>
|
||||
|
|
@ -203,7 +205,6 @@
|
|||
</div>
|
||||
|
||||
<div class="py-4"></div>
|
||||
|
||||
<div
|
||||
class="grid place-content-center place-items-center pointer-events-auto font-mono"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { onMount } from "svelte";
|
||||
import onMount from "@e/onMount";
|
||||
|
||||
export let images: string[] = []; // Expose images as a parameter
|
||||
let currentIndex: number = 0;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
<script lang="ts">
|
||||
import { onMount, onDestroy } from "svelte";
|
||||
import onMount from "@e/onMount";
|
||||
import onDestroy from "@e/onDestroy";
|
||||
import MediaQuery from "svelte-media-queries";
|
||||
|
||||
// Public props
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script>
|
||||
import { onMount } from "svelte";
|
||||
import onMount from "@e/onMount";
|
||||
|
||||
let debug = false;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { onMount } from "svelte";
|
||||
import onMount from "@e/onMount";
|
||||
import { Vector2 } from "../zhen/Utils/Vector2";
|
||||
|
||||
// Params
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
<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";
|
||||
import onMount from "@e/onMount";
|
||||
import onDestroy from "@e/onDestroy";
|
||||
import ArrowBigDown from "lucide-svelte/icons/arrow-big-down";
|
||||
import fly from "@e/fly";
|
||||
import re from "@ts/Redaction/Redactor";
|
||||
const buildTime = __BUILD_TIME__;
|
||||
|
||||
let scrollY = 0;
|
||||
|
|
@ -156,16 +157,16 @@
|
|||
/>
|
||||
|
||||
<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>
|
||||
<!-- <!-- Keep scrolling thing --> -->
|
||||
<!-- <div class="hidden h-64 w-full flex flex-col justify-center items-center"> -->
|
||||
<!-- <div>Keep scrolling to veiw [Redacted]'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"
|
||||
|
|
@ -193,7 +194,7 @@
|
|||
<br />
|
||||
<span>Snorre Ettrup Altschul</span>
|
||||
<br />
|
||||
<span>Zhentao Wei</span>
|
||||
<span>{$re?.name ?? "BOT Alex"}</span>
|
||||
</div>
|
||||
<div class="flex flex-col items-center">
|
||||
<h3><b>About this website</b></h3>
|
||||
|
|
@ -221,7 +222,9 @@
|
|||
</div>
|
||||
<div class="flex flex-col items-center">
|
||||
<h3><b>Contact</b></h3>
|
||||
<a href="mailto:zhen@deprived.dev">zhen@deprived.dev</a>
|
||||
<a href="mailto:{$re?.email ?? 'Alex@deprived.dev'}"
|
||||
>{$re?.email ?? "alex@deprived.dev"}</a
|
||||
>
|
||||
<div class="mt-2"></div>
|
||||
<a
|
||||
href="https://discord.gg/awatEEqc3M"
|
||||
|
|
|
|||
|
|
@ -1,30 +1,30 @@
|
|||
<script lang="ts">
|
||||
import re from "@ts/Redaction/Redactor";
|
||||
|
||||
// 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";
|
||||
import NameAndImage from "./comps/NameAndImage.svelte";
|
||||
import ShortProfile from "./comps/ShortProfile.svelte";
|
||||
import CombinedContacts from "./comps/CombinedContacts.svelte";
|
||||
import LinkedInQR from "./comps/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";
|
||||
import Profile from "./comps/Profile.svelte";
|
||||
import Experience from "./comps/Experience.svelte";
|
||||
import Education from "./comps/Education.svelte";
|
||||
import BiggestFlex from "./comps/BiggestFlex.svelte";
|
||||
import TableOfProjects from "./comps/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";
|
||||
|
||||
import LeftTopDecor from "./comps/LeftTopDecor.svelte";
|
||||
import BottomRightDecor from "./comps/BottomRightDecor.svelte";
|
||||
import AlexWatermark from "./comps/AlexWatermark.svelte";
|
||||
import RepeatedSkills from "./comps/RepeatedSkills.svelte";
|
||||
|
||||
// Discord embed
|
||||
import preveiwImage from "$lib/zhen/cv-comps/EposCvPreveiw.png";
|
||||
import preveiwImage from "$lib/alex/cv-comps/preview.png";
|
||||
|
||||
// Print detection setup
|
||||
import { onMount } from "svelte";
|
||||
import onMount from "@e/onMount";
|
||||
onMount(() => {
|
||||
// Check if the query parameter exists in the URL
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
|
|
@ -46,8 +46,8 @@
|
|||
}
|
||||
</script>
|
||||
|
||||
<title>Zhentao Wei's CV {getFormattedDate()}</title>
|
||||
<meta content="Zhentao Wei's CV" property="og:title" />
|
||||
<title>{$re?.name ?? "Alex"}'s CV {getFormattedDate()}</title>
|
||||
<meta content="{$re?.name ?? 'Alex'}'s CV" property="og:title" />
|
||||
<meta
|
||||
content="This CV is made completely with svelte + html + css + js"
|
||||
property="og:description"
|
||||
|
|
@ -75,7 +75,7 @@
|
|||
|
||||
<div class="NotoSans cv-config cv-container-container include-in-print">
|
||||
<div class="cv-container sections decorations">
|
||||
<div id="left-section" class="bg-grid-cv">
|
||||
<div id="left-section" class="bg-grid-cv flex justify-center">
|
||||
<LeftTopDecor />
|
||||
<BottomRightDecor Style="pointer-events: none;" />
|
||||
<div
|
||||
|
|
@ -83,7 +83,9 @@
|
|||
style="background-color: #bdd6ee"
|
||||
></div>
|
||||
<div class="text-[var(--left-text-color)] pointer-events-none">
|
||||
<div class="pointer-events-auto flex flex-col justify-center w-full items-center">
|
||||
<div
|
||||
class="pointer-events-auto flex flex-col justify-center w-full items-center"
|
||||
>
|
||||
<NameAndImage />
|
||||
<div class="py-2"></div>
|
||||
<ShortProfile />
|
||||
|
|
@ -116,7 +118,6 @@
|
|||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
.cv-config * {
|
||||
--left-text-color: #eeeeee;
|
||||
--left-line-color: #999999;
|
||||
|
|
@ -131,22 +132,33 @@
|
|||
--left-grid-opacity: 0.1;
|
||||
--left-grid-size: 10px;
|
||||
|
||||
|
||||
--right-text-color: #333333;
|
||||
}
|
||||
|
||||
.corner-border-container {
|
||||
background-color: var(--left-grid-bg-color);
|
||||
background-image:
|
||||
linear-gradient(var(--left-decor-line-color), var(--left-decor-line-color)),
|
||||
linear-gradient(var(--left-decor-line-color), var(--left-decor-line-color)),
|
||||
linear-gradient(var(--left-decor-line-color), var(--left-decor-line-color)),
|
||||
linear-gradient(var(--left-decor-line-color), var(--left-decor-line-color));
|
||||
background-size:
|
||||
30px 4px, /* top-left horizontal */
|
||||
4px 30px, /* top-left vertical */
|
||||
30px 4px, /* bottom-right horizontal */
|
||||
4px 30px; /* bottom-right vertical */
|
||||
linear-gradient(
|
||||
var(--left-decor-line-color),
|
||||
var(--left-decor-line-color)
|
||||
),
|
||||
linear-gradient(
|
||||
var(--left-decor-line-color),
|
||||
var(--left-decor-line-color)
|
||||
),
|
||||
linear-gradient(
|
||||
var(--left-decor-line-color),
|
||||
var(--left-decor-line-color)
|
||||
),
|
||||
linear-gradient(
|
||||
var(--left-decor-line-color),
|
||||
var(--left-decor-line-color)
|
||||
);
|
||||
background-size:
|
||||
30px 4px,
|
||||
/* top-left horizontal */ 4px 30px,
|
||||
/* top-left vertical */ 30px 4px,
|
||||
/* bottom-right horizontal */ 4px 30px; /* bottom-right vertical */
|
||||
background-repeat: no-repeat;
|
||||
background-position:
|
||||
top left,
|
||||
|
|
@ -157,8 +169,16 @@
|
|||
|
||||
.bg-grid-cv {
|
||||
background:
|
||||
linear-gradient(-90deg, rgba(255, 255, 255, var(--left-grid-opacity)) 1px, transparent 1px),
|
||||
linear-gradient(rgba(255, 255, 255, var(--left-grid-opacity)) 1px, transparent 1px), var(--left-grid-line-color);
|
||||
linear-gradient(
|
||||
-90deg,
|
||||
rgba(255, 255, 255, var(--left-grid-opacity)) 1px,
|
||||
transparent 1px
|
||||
),
|
||||
linear-gradient(
|
||||
rgba(255, 255, 255, var(--left-grid-opacity)) 1px,
|
||||
transparent 1px
|
||||
),
|
||||
var(--left-grid-line-color);
|
||||
background-size:
|
||||
var(--left-grid-size) var(--left-grid-size),
|
||||
var(--left-grid-size) var(--left-grid-size),
|
||||
|
|
@ -202,7 +222,6 @@
|
|||
place-items: center;
|
||||
}
|
||||
|
||||
|
||||
.cv-container {
|
||||
width: 210mm;
|
||||
height: 297mm;
|
||||
|
|
@ -230,13 +249,10 @@
|
|||
|
||||
> div:last-child {
|
||||
z-index: 1;
|
||||
width: 100%;
|
||||
width: 17.5rem;
|
||||
|
||||
left: 0;
|
||||
|
||||
display: grid;
|
||||
place-items: center;
|
||||
|
||||
padding-top: 30mm;
|
||||
padding-bottom: 30mm;
|
||||
}
|
||||
|
|
@ -300,7 +316,6 @@
|
|||
font-family: "CozetteVector";
|
||||
font-size: x-large;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
</style>
|
||||
81
src/routes/cv/comps/Contact.svelte
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
<script lang="ts">
|
||||
import re from "@ts/Redaction/Redactor";
|
||||
</script>
|
||||
|
||||
<div class="container">
|
||||
<div>
|
||||
<b style="text-align:left;"> Contact </b>
|
||||
</div>
|
||||
<div class="table-display">
|
||||
<div class="table-item">
|
||||
<div>Email</div>
|
||||
<div>{$re?.email ?? "alex@deprived.dev"}</div>
|
||||
</div>
|
||||
<div class="table-item">
|
||||
<div>Phone</div>
|
||||
<div>{$re?.phone ?? "1-800-273-8255"}</div>
|
||||
</div>
|
||||
<div class="table-item">
|
||||
<div>LinkedIn</div>
|
||||
<a
|
||||
href={$re?.linkedIn.link ??
|
||||
"https://www.youtube.com/watch?v=PaPotS8GSpc"}
|
||||
>{$re?.linkedIn.text ?? "cool video lmao"}</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 var(--left-line-color);
|
||||
}
|
||||
|
||||
.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 {
|
||||
&: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>
|
||||
|
|
@ -1,11 +1,14 @@
|
|||
<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 SasLogo from "$lib/alex/cv-comps/SASLogo.png";
|
||||
|
||||
import IconAndText2 from "./IconAndText2.svelte";
|
||||
import re from "@src/ts/Redaction/Redactor";
|
||||
import env, { initEnv } from "@src/ts/EnvHandler";
|
||||
import onMount from "@src/optimizers/onMount";
|
||||
|
||||
onMount(() => {
|
||||
initEnv();
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="container h-10">
|
||||
|
|
@ -13,20 +16,22 @@
|
|||
<b style="text-align:left;"> Education </b>
|
||||
</div>
|
||||
<div class="flex justify-center p-2 w-full">
|
||||
<IconAndText2 logo={DTU_Logo}>
|
||||
<b>DTU</b><br />
|
||||
<IconAndText2
|
||||
logo={$re?.education[0].imageId.replace("[PB]", env.POCKETBASE_URL) ?? ""}
|
||||
>
|
||||
<b>{$re?.education[0].name ?? ""}</b><br />
|
||||
<p style="font-size: 0.5rem;">AI and data</p>
|
||||
</IconAndText2>
|
||||
<IconAndText2 logo={NextLogo}>
|
||||
<b>Next</b><br />
|
||||
<IconAndText2 logo={$re?.education[1].imageId ?? ""}>
|
||||
<b>{$re?.education[0].name ?? ""}</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 />
|
||||
<IconAndText2 logo={$re?.education[2].imageId ?? ""}>
|
||||
<span class="font-semibold">{$re?.education[2].name ?? ""}</span><br />
|
||||
<p style="font-size: 0.5rem;">VR development</p>
|
||||
</IconAndText2>
|
||||
</div>
|
||||
|
|
@ -44,4 +49,3 @@
|
|||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
|
|
@ -1,11 +1,5 @@
|
|||
<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 re from "@src/ts/Redaction/Redactor";
|
||||
import IconAndText from "./IconAndText.svelte";
|
||||
</script>
|
||||
|
||||
|
|
@ -15,37 +9,37 @@
|
|||
</div>
|
||||
<div class="table">
|
||||
<div class="table-item">
|
||||
<IconAndText logo={YaaummaLogo}>
|
||||
<IconAndText logo={$re?.experience[0].imageId ?? ""}>
|
||||
<b>Full-stack</b><br />
|
||||
Yaaumma<br />
|
||||
{$re?.experience[0].name}<br />
|
||||
<i>Feb 2025 - Now</i>
|
||||
</IconAndText>
|
||||
</div>
|
||||
<div class="table-item">
|
||||
<IconAndText logo={GrazperAILogo}>
|
||||
<IconAndText logo={$re?.experience[1].imageId ?? ""}>
|
||||
<b>Data annotator</b><br />
|
||||
GrazperAI<br />
|
||||
{$re?.experience[1].name}<br />
|
||||
<i>Jul 2024 - Now</i>
|
||||
</IconAndText>
|
||||
</div>
|
||||
<div class="table-item">
|
||||
<IconAndText logo={MakerspaceLogo}>
|
||||
<IconAndText logo={$re?.experience[2].imageId ?? ""}>
|
||||
<b>3D printer manager</b> - Volunteer<br />
|
||||
Makerspace - kildevæld Kulturcenter<br />
|
||||
{$re?.experience[2].name}<br />
|
||||
<i>Nov 2023 - Now</i>
|
||||
</IconAndText>
|
||||
</div>
|
||||
<div class="table-item">
|
||||
<IconAndText logo={EposLogo}>
|
||||
<IconAndText logo={$re?.experience[3].imageId ?? ""}>
|
||||
<b>Machine Learning Engineer</b> - Short term intern<br />
|
||||
Product design department - Epos<br />
|
||||
{$re?.experience[3].name}<br />
|
||||
<i>Apr 2024 - Apr 2024</i>
|
||||
</IconAndText>
|
||||
</div>
|
||||
<div class="table-item">
|
||||
<IconAndText logo={KhoraLogo}>
|
||||
<IconAndText logo={$re?.experience[4].imageId ?? ""}>
|
||||
<b>Assistant</b> - Short term intern<br />
|
||||
Khora Virtual Reality<br />
|
||||
{$re?.experience[4].name}<br />
|
||||
<i>Oct 2020 - Oct 2020</i>
|
||||
</IconAndText>
|
||||
</div>
|
||||
|
|
@ -5,9 +5,12 @@
|
|||
export let fontSize: string = "3mm";
|
||||
export let lineHeight: string = "3.1mm";
|
||||
|
||||
import { onMount } from "svelte";
|
||||
import env, { initEnv } from "@src/ts/EnvHandler";
|
||||
|
||||
import onMount from "@e/onMount";
|
||||
onMount(() => {
|
||||
imageCaption = logo.split(/(\\|\/)/g).pop();
|
||||
initEnv();
|
||||
});
|
||||
|
||||
let imageCaption: undefined | string; // Not a high piority, you get the file name and thats it
|
||||
|
|
@ -15,7 +18,7 @@
|
|||
|
||||
<div class="container">
|
||||
<img
|
||||
src={logo}
|
||||
src={logo.replace("[PB]", env.POCKETBASE_URL) ?? ""}
|
||||
class="bg-white w-10 h-10 object-contain rounded shadow"
|
||||
alt={imageCaption}
|
||||
width={logoWidths}
|
||||
|
|
@ -37,7 +40,5 @@
|
|||
|
||||
text-align: start;
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
|
||||
|
|
@ -2,10 +2,11 @@
|
|||
export let logo: string;
|
||||
export let logoWidths: string = "35%";
|
||||
|
||||
|
||||
import { onMount } from "svelte";
|
||||
import onMount from "@e/onMount";
|
||||
import env, { initEnv } from "@src/ts/EnvHandler";
|
||||
onMount(() => {
|
||||
imageCaption = logo.split(/(\\|\/)/g).pop();
|
||||
initEnv();
|
||||
});
|
||||
|
||||
let imageCaption: undefined | string; // Not a high piority, you get the file name and thats it
|
||||
|
|
@ -14,18 +15,15 @@
|
|||
<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}
|
||||
src={logo.replace("[PB]", env.POCKETBASE_URL) ?? ""}
|
||||
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 " >
|
||||
<div class="px-1 ml-1 h-full text-[0.5rem]">
|
||||
<span class="inline">
|
||||
<slot />
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
75
src/routes/cv/comps/LeftTopDecor.svelte
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
<script>
|
||||
import RepeatedSkills from "./RepeatedSkills.svelte";
|
||||
|
||||
// Cedit
|
||||
import LinkToSource from "./LinkToSource.svelte";
|
||||
|
||||
export let Style = "";
|
||||
export let Class = "";
|
||||
</script>
|
||||
|
||||
<div class="container {Class}" style={Style}>
|
||||
<div class=" text-center bg-[var(--left-grid-bg-color)]">
|
||||
<RepeatedSkills
|
||||
textOverride={["Hello", "你好", "Hej"]}
|
||||
targetTextHeight={3}
|
||||
targetTextWidth={50}
|
||||
applyRotation={false}
|
||||
/>
|
||||
</div>
|
||||
<div />
|
||||
<div class="flex justify-center">
|
||||
<div class="w-[6cm]">
|
||||
<LinkToSource />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.container {
|
||||
position: absolute;
|
||||
transform: translate(-30mm, 7.5mm) 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;
|
||||
padding-top: 1mm;
|
||||
|
||||
// Text inside
|
||||
display: grid;
|
||||
place-content: center;
|
||||
border-bottom: var(--left-decor-line-color) dotted 0.5mm;
|
||||
border-top: var(--left-decor-line-color) dotted 0.5mm;
|
||||
|
||||
&:first-child {
|
||||
color: var(--left-decor-text-color);
|
||||
font-size: 3mm;
|
||||
//font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
> div:nth-child(2) {
|
||||
padding-top: 4mm;
|
||||
//border-bottom: #4472c4 dashed 2mm;
|
||||
|
||||
// background-color: var(--left-grid-bg-color);
|
||||
|
||||
background-image: linear-gradient(
|
||||
to right,
|
||||
var(--left-decor-line-color) 70%,
|
||||
rgba(255, 255, 255, 0) 0%
|
||||
);
|
||||
background-position: bottom;
|
||||
background-size: 6mm 1.5mm;
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
50
src/routes/cv/comps/LinkToSource.svelte
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
<script lang="ts">
|
||||
import svelteLogo from "$lib/svelteLogos/svelte-logo-cutout.svg";
|
||||
import onMount from "@src/optimizers/onMount";
|
||||
import re from "@src/ts/Redaction/Redactor";
|
||||
</script>
|
||||
|
||||
<div class="container">
|
||||
<div class="flex justify-center">
|
||||
<div class="corner-border-container p-1 m-1">
|
||||
<div class="flex">
|
||||
This CV was made using html, css and <a
|
||||
class="grid place-content-center"
|
||||
href="https://kit.svelte.dev/"
|
||||
><img src={svelteLogo} class="w-2 h-2" alt="SvelteKit logo" /></a
|
||||
>
|
||||
</div>
|
||||
Sources:
|
||||
<a
|
||||
href={$re?.cv.sourceLink ??
|
||||
"https://www.youtube.com/watch?v=0TaNezk4wNQ"}>CV source code</a
|
||||
>
|
||||
and
|
||||
<a href="/cv?hideOnPrint=1">My Website</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.container {
|
||||
z-index: 1;
|
||||
|
||||
font-size: 0.5rem;
|
||||
|
||||
//white-space: nowrap;
|
||||
|
||||
color: #777777;
|
||||
|
||||
* a {
|
||||
color: #3d6ddc;
|
||||
padding-left: 1mm;
|
||||
padding-right: 1mm;
|
||||
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
div:nth-child(2) {
|
||||
padding-bottom: 2mm;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
105
src/routes/cv/comps/LinkedInQR.svelte
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
<script lang="ts">
|
||||
import re from "@src/ts/Redaction/Redactor";
|
||||
|
||||
// Gave up because a little drunk, so rest is chatgpt
|
||||
|
||||
function getHTML(url: string): Promise<string> {
|
||||
return fetch(url, { method: "GET", headers: { Accept: "text/html" } }).then(
|
||||
(res) => {
|
||||
if (!res.ok)
|
||||
throw new Error(`GET ${url} failed: ${res.status} ${res.statusText}`);
|
||||
return res.text();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// recompute the promise when the (store-derived) URL changes
|
||||
$: url = $re?.linkedIn?.imageId || "";
|
||||
$: htmlPromise = url ? getHTML(url) : Promise.resolve("");
|
||||
</script>
|
||||
|
||||
<div class="container">
|
||||
<div>LinkedIn</div>
|
||||
<div class="qrcode corner-border-container p-4">
|
||||
{#await htmlPromise}
|
||||
<span>Loading…</span>
|
||||
{:then html}
|
||||
{@html html}
|
||||
{:catch err}
|
||||
<span class="text-red-600">{err.message}</span>
|
||||
{/await}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.corner-border-container {
|
||||
--length: 20px;
|
||||
--width: 4px;
|
||||
background-color: var(--left-grid-bg-color);
|
||||
background-image:
|
||||
linear-gradient(
|
||||
var(--left-decor-line-color),
|
||||
var(--left-decor-line-color)
|
||||
),
|
||||
linear-gradient(
|
||||
var(--left-decor-line-color),
|
||||
var(--left-decor-line-color)
|
||||
),
|
||||
linear-gradient(
|
||||
var(--left-decor-line-color),
|
||||
var(--left-decor-line-color)
|
||||
),
|
||||
linear-gradient(
|
||||
var(--left-decor-line-color),
|
||||
var(--left-decor-line-color)
|
||||
),
|
||||
linear-gradient(
|
||||
var(--left-decor-line-color),
|
||||
var(--left-decor-line-color)
|
||||
),
|
||||
linear-gradient(
|
||||
var(--left-decor-line-color),
|
||||
var(--left-decor-line-color)
|
||||
),
|
||||
linear-gradient(
|
||||
var(--left-decor-line-color),
|
||||
var(--left-decor-line-color)
|
||||
),
|
||||
linear-gradient(
|
||||
var(--left-decor-line-color),
|
||||
var(--left-decor-line-color)
|
||||
);
|
||||
background-size:
|
||||
var(--length) var(--width),
|
||||
var(--width) var(--length),
|
||||
var(--length) var(--width),
|
||||
var(--width) var(--length),
|
||||
var(--length) var(--width),
|
||||
var(--width) var(--length),
|
||||
var(--length) var(--width),
|
||||
var(--width) var(--length);
|
||||
background-position:
|
||||
top left,
|
||||
top left,
|
||||
top right,
|
||||
top right,
|
||||
bottom right,
|
||||
bottom right,
|
||||
bottom left,
|
||||
bottom left;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.qrcode {
|
||||
transform: scale(0.9);
|
||||
}
|
||||
|
||||
.container {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
|
||||
& * {
|
||||
font-size: 7.5mm;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
24
src/routes/cv/comps/NameAndImage.svelte
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
<script lang="ts">
|
||||
import NamePlate from "./NamePlate.svelte";
|
||||
import selfie from "$lib/alex/cv-comps/VRNerd.jpg";
|
||||
</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="Selfie" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.nameAndImageContainer {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
}
|
||||
|
||||
.selfie-constraints {
|
||||
max-width: 100%;
|
||||
}
|
||||
</style>
|
||||
21
src/routes/cv/comps/NamePlate.svelte
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
<script lang="ts">
|
||||
import re from "@ts/Redaction/Redactor";
|
||||
</script>
|
||||
|
||||
<div class="name-plate-container">
|
||||
<span style="text-align: center;">
|
||||
<b>{$re?.name ?? "BOTAlex"}</b><br />
|
||||
(He/Him)
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.name-plate-container {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
width: 60%;
|
||||
|
||||
/* Bottom border stripe*/
|
||||
border-bottom: 1mm solid var(--left-line-color);
|
||||
}
|
||||
</style>
|
||||
75
src/routes/cv/comps/OtherContact.svelte
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
<script lang="ts">
|
||||
import re from "@ts/Redaction/Redactor";
|
||||
</script>
|
||||
|
||||
<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={$re?.itch.link ?? "https://www.youtube.com/watch?v=PaPotS8GSpc"}
|
||||
>{$re?.itch.text ?? "The same video"}</a
|
||||
>
|
||||
</div>
|
||||
<div class="table-item">
|
||||
<div>Github</div>
|
||||
<a href="https://github.com/MagicBOTAlex">@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 var(--left-line-color);
|
||||
}
|
||||
|
||||
.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 {
|
||||
&: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>
|
||||
37
src/routes/cv/comps/Profile.svelte
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
<div class="short-profile-container">
|
||||
<div class="flex gap-1 items-baseline">
|
||||
<b style="text-align:left;"> About me </b>
|
||||
<div class="opacity-70 text-[0.5rem]">
|
||||
I know the languages listed above in the decor!
|
||||
</div>
|
||||
</div>
|
||||
<span>
|
||||
I love learning on my own. I've placed my education at the bottom of my CV.
|
||||
This is because everything I've learnt is on my own, and I refuse to give
|
||||
too much credit to my education. I encurage you to checkout which <span
|
||||
class="font-semibold">Open-source</span
|
||||
>
|
||||
projects I've worked on, on my
|
||||
<a href="https://github.com/MagicBOTAlex"
|
||||
><span class="text-blue-500 underline">github</span></a
|
||||
>.
|
||||
<br />
|
||||
<br />
|
||||
Other than my github, I've listed some projects highlighted below. :)
|
||||
</span>
|
||||
</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>
|
||||
32
src/routes/cv/comps/ShortProfile.svelte
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
<script lang="ts">
|
||||
import re from "@ts/Redaction/Redactor";
|
||||
</script>
|
||||
|
||||
<div class="short-profile-container">
|
||||
<div>
|
||||
<b style="text-align:left;"> Short profile </b>
|
||||
</div>
|
||||
<div>
|
||||
◾ Full-stack at {$re?.shortProfileHiddenContent[0] ?? "Deprived devs"}
|
||||
<br />
|
||||
◾ "AI and data" at {$re?.shortProfileHiddenContent[1] ?? "some uni"}.
|
||||
<br />
|
||||
◾ Working at {$re?.shortProfileHiddenContent[2] ?? "somewhere"} <br />
|
||||
◾ Volunteer at {$re?.shortProfileHiddenContent[3] ?? "Deprived devs"}.
|
||||
</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 var(--left-line-color);
|
||||
}
|
||||
</style>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
<div class="public-cv-container">
|
||||
<h1 class="bottom-dotted-line">Public CVs</h1>
|
||||
<div style="padding-left: 0.5vw; margin-top: -1vh;">
|
||||
<b>Rev1: </b><a href="/zhen/cv/rev1?hideOnPrint=1" style="color:lightblue;">Rev1.pdf</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.public-cv-container {
|
||||
padding-left: 10vw;
|
||||
padding-top: 5vh;
|
||||
width: 79vw;
|
||||
}
|
||||
|
||||
.bottom-dotted-line {
|
||||
border-bottom: 4px dotted rgb(178, 178, 178);
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
<script lang="ts">
|
||||
import HorizonalStack from './../../../comps/Utils/HorizonalStack.svelte'
|
||||
</script>
|
||||
|
||||
<div class="container">
|
||||
<div style="flex-grow: 1;" />
|
||||
<div class="TextContainer">
|
||||
<span class="NamePlateText" >
|
||||
Zhentao Wei
|
||||
<br/>
|
||||
<p class="NickNameText">Alex</p>
|
||||
</span>
|
||||
</div>
|
||||
<div style="flex-grow: 2;" />
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.container{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
align-items: center;
|
||||
vertical-align: middle;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.TextContainer{
|
||||
align-items: center;
|
||||
vertical-align: middle;
|
||||
display: flex;
|
||||
|
||||
/* background-color: aliceblue; */
|
||||
background-color: rgba(45, 45, 45, 0.645);
|
||||
padding: 0.75rem 1rem;
|
||||
border-radius: 20px;
|
||||
|
||||
backdrop-filter: blur(1px);
|
||||
}
|
||||
|
||||
.NamePlateText{
|
||||
/* font-family: 'CozetteVector'; */
|
||||
text-align: left;
|
||||
font-size: 300%;
|
||||
color: rgb(225, 225, 225);
|
||||
|
||||
border-left: 0.5rem solid rgb(88, 198, 82);
|
||||
padding-left: 1rem;
|
||||
}
|
||||
|
||||
.NickNameText{
|
||||
font-size: 75%;
|
||||
margin: 0;
|
||||
margin-top: -1rem;
|
||||
|
||||
color: rgb(99, 99, 99);
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,67 +0,0 @@
|
|||
<script lang="ts">
|
||||
import Timeline from "../../../comps/timeline/timeline.svelte";
|
||||
import TimelineItem from "../../../comps/timeline/timelineItem.svelte";
|
||||
</script>
|
||||
|
||||
<Timeline style="padding: 2rem; padding-left: 3vw; padding-bottom: 5rem;">
|
||||
<TimelineItem
|
||||
date="3.G 10/3-20234"
|
||||
title="Prolog Apple device recommendation"
|
||||
desc='Et program lavet med Prolog, som finder en tilpassende Apple enhed baseret på dato udgivet og pris af enheden. <br/><br/>Link: <a href="/portfolios/alex/Informatik3g2g/Prolog-Rapport-1.pdf" style="color:lightblue;">Rapport</a>'
|
||||
imagePath="/portfolios/sveske/prolog/thumb.png"
|
||||
/>
|
||||
<TimelineItem
|
||||
date="3.G 3/12-2023"
|
||||
title="Extraordinær menneskelig sans"
|
||||
desc='Med brug af en M5StickCPlus og en bevægelses sensor, så skabte benjamin og jeg en ekstra menneskelig sans.<br/><br/>Link: <a href="/portfolios/alex/Informatik3g2g/HumanInterface - Rapport-1.pdf" style="color:lightblue;">Rapport</a>'
|
||||
imagePath="/portfolios/sveske/hmi/thumb.png"
|
||||
/>
|
||||
<TimelineItem
|
||||
date="2.G 16/6-2023"
|
||||
title="Otakians"
|
||||
desc='Dette projekt repræsenterer apogeeen af mine anstrengelser og stræben inden for teknologisk innovation. Det er en avanceret webapplikation konstrueret med en sofistikeret arkitektur, der omfatter HTML, CSS, C#, og javascript som fundamentale byggesten. Indlejret i dets omfangsrige struktur findes et imponerende repertoire af funktioner, herunder en grundig præsentationssektion, en dynamisk chatmodul, en omfattende FAQ-sektion og et stringent, indbygget login-system. Projektets kerneformål er at fungere som et digitalt samlingspunkt for tilhængere af anime, kendt som "weebs", hvor de kan dele, diskutere og opbygge fællesskaber om deres passion for denne japanske popkulturform. Med en sofistikeret integration af komplekse algoritmer og brugercentrerede designprincipper stræber denne hjemmeside mod at skabe en opslidende og meningsfuld oplevelse for sine brugere, og dermed forstærke forbindelserne inden for dette særlige interessefællesskab. <br/><br/>Link: <a href="/portfolios/alex/Informatik3g2g/Otakians(1).docx" style="color:lightblue;">Rapport</a>'
|
||||
imagePath="/images/Zhen/Infomatik/Otakians.png"
|
||||
/>
|
||||
<TimelineItem
|
||||
date="2.G 10/3-2023"
|
||||
title="Makey Makey: pressure plate"
|
||||
desc="Ved anvendelse af Makey Makey har vi skabt en innovativ og funktionel trykplade af papir, tape, aluminiumsfolie og selve Makey Makey-enheden. Denne teknologiske opfindelse udgør et paradigmeskift inden for DIY-elektronik og inkorporerer en særegen symbiose af håndværksmæssig finesse og digital innovation. Ved at omfavne en konvergens af analoge og digitale teknologier demonstrerer denne opfindelse et ekstraordinært niveau af teknisk snilde og kreativ tænkning. Gennem en kompleks integration af disse elementer opnår denne trykplade ikke blot funktionalitet, men repræsenterer også en manifestation af menneskelig opfindsomhed og teknologisk fremgang. <br/><br/>Link: <a href='https://www.youtube.com/watch?v=AIUuqfbgKRw' style='color:lightblue;'>Youtube video</a><br/>Link 2: <a href='https://www.youtube.com/watch?v=tJ_N_K_kbjw' style='color:lightblue;'>Youtube video</a> <br/><br/>Link: <a href='/portfolios/alex/Informatik3g2g/PressurePlate.pdf' style='color:lightblue;'>Rapport</a>"
|
||||
imagePath="/images/Zhen/Infomatik/PressurePlate.png"
|
||||
/>
|
||||
<TimelineItem
|
||||
date="2.G 11/9-2022"
|
||||
title="Visit copenhagen"
|
||||
desc="Dette projekt inkorporerede en mangefacetteret tilgang med adskillige elementer af gestaltloven, og det udnyttede HTML og CSS til at animere disse aspekter. Den valgte ramme, .NET Blazor, fungerede som fundamentet for implementeringen af disse avancerede visuelle og interaktive komponenter. Gennem en dybdegående anvendelse af komplekse gestaltprincipper blev en symbiotisk syntese opnået, hvilket resulterede i en dynamisk digital oplevelse, der strømlinede brugerinteraktionen og forbedrede æstetikken af det endelige produkt. <br/><br/>Link: <a href='https://youtu.be/S9Rpm12Al0Y' style='color:lightblue;'>Youtube video</a> <br/><br/>Link: <a href='/portfolios/alex/Informatik3g2g/VisitCopenhagenRapport(1).docx' style='color:lightblue;'>Rapport</a>"
|
||||
imagePath="/images/Zhen/Infomatik/VisitCopenhagen.png"
|
||||
/>
|
||||
<TimelineItem
|
||||
date="2.G 2022"
|
||||
title="Uncle roger's recipies"
|
||||
desc="Denne sekundære webplatform, udspringende af mine digitale kreationer, udfolder sig som en kyndig panorama over de bedste 5 opskrifter præsenteret af Uncle Rogers. Udformet med pur HTML og anstrøg af CSS, emmer den af enkelhed kombineret med subtil elegance, og inkorporerer diskrete CSS-animationer for at tilføje en dynamisk dimension til brugeroplevelsen. Med en nøje sammensat struktur og en stringent implementering af webteknologier, udforsker den dybden af kulinariske inspirationer fra den anerkendte kogekunstner, alt imens den søger at fremme gastronomisk kreativitet og forfinelse.<br/><br/>Link: <a href='/portfolios/alex/Informatik3g2g/AsiatiskMad.html' style='color:lightblue;'>Rapport</a>"
|
||||
imagePath="/images/Zhen/Infomatik/UncleRogers.png"
|
||||
/>
|
||||
<TimelineItem
|
||||
date="2.G 2022"
|
||||
title="Game rankings website"
|
||||
desc="Denne pionerende webside, skabt med hovedsageligt HTML og et minimængde af CSS, repræsenterer en banebrydende praksis i webudviklingen. Ved at begrænse brugen af CSS til et minimum og ved at undlade JavaScript, demonstrerer den en avantgarde tilgang til design og funktionalitet. Med en fokuseret brug af HTML som kerneelement til opbygning af websiden, fremhæver den vigtigheden af semantisk strukturering og tilgængelighed. Denne minimalistiske tilgang afspejler en dyb forståelse for webudviklingens essentielle principper og skaber samtidig en unik æstetik og brugeroplevelse, der inspirerer til refleksion over konventionelle praksisser inden for digital design.<br/><br/>Link: <a href='/portfolios/alex/Informatik3g2g/SpilHjemmeside.html' style='color:lightblue;'>Rapport</a>"
|
||||
imagePath="/images/Zhen/Infomatik/FirstWebsite.png"
|
||||
/>
|
||||
<TimelineItem
|
||||
date="1.G 2022"
|
||||
title="Quest for grass"
|
||||
desc="Dette avancerede projekt udvikler et dynamisk spil i Unity-miljøet med en sofistikeret integration af en database, der skrupelløst opbevarer og manipulerer brugerens præstationer. Den benyttede database struktureres og drives af den altopslugende Google Firebase-platform, der leverer en robust og skalerbar infrastruktur til håndtering af data. Gennem en sublim kombination af komplekse algoritmer og interaktionelle mekanismer, optimeres brugeroplevelsen, og spillets dybde forøges betragteligt. Dette projekt fremhæver det symbiotiske forhold mellem teknologiske avancer og kreative visioner, idet det udforsker nye horisonter inden for spiludvikling og datamanagement. <br/><br/>Link: <a href='https://youtu.be/Vb_2U2lyVFY' style='color:lightblue;'>Youtube video</a> <br/><br/>Link: <a href='/portfolios/alex/Informatik1g/DatabaseProjektTouchGrass.docx' style='color:lightblue;'>Rapport</a>"
|
||||
imagePath="/images/Zhen/Infomatik/QuestForGrass.png"
|
||||
/>
|
||||
<TimelineItem
|
||||
date="1.G 2022"
|
||||
desc="Denne applikation, konstrueret i AppLab, agerer som en præcis katalysator, der faciliterer forståelsen af de 13 globale bæredygtighedsmål fastsat af De Forenede Nationer. Med en intuitiv brugergrænseflade og en enestående funktionalitet præsenterer den en omfattende oversigt over disse komplekse mål, hvilket muliggør en dybdegående indsigt i de multidimensionelle aspekter af global udvikling. Med en elegant integration af avancerede algoritmer og interaktive elementer guider denne app brugeren gennem et labyrintisk landskab af komplekse socioøkonomiske og miljømæssige koncepter, hvilket fremmer en dybere forståelse og engagement i bestræbelserne på at opnå en mere bæredygtig planet.<br/><br/>Link: <a href='/portfolios/alex/Informatik1g/AppLabRapport(1).docx' style='color:lightblue;'>Rapport</a>"
|
||||
title="App lab verdensmål"
|
||||
imagePath="/images/Zhen/Infomatik/1.G-App.png"
|
||||
/>
|
||||
<TimelineItem
|
||||
date="1.G 2021"
|
||||
desc='Dette initiativ involverede implementeringen af et avanceret værktøj fra Google kendt som "teachable machine", som blev konfigureret til at identificere en række håndudtryk. Med en kompleks kombination af maskinlæringsteknikker og datadrevne algoritmer blev dette projekt realiseret med ekspertise og præcision. Ved at udnytte avancerede neurale netværk og dybdegående datasæt muliggjorde dette værktøj en dybtgående analyse af de subtile nuancer i håndgester, hvilket resulterede i en imponerende nøjagtighed og pålidelighed i genkendelsen af disse udtryk. Dette projekt repræsenterer et skridt fremad i feltet for computer vision og maskinlæring, og det illustrerer potentialet i at anvende avancerede teknologier til at løse komplekse problemer inden for menneske-maskine-interaktion. <br/><br/>Link: <a href="/portfolios/alex/Informatik1g/MachineLearningRapport.docx" style="color:lightblue;">Rapport</a>'
|
||||
title="Machine learning: gesture recognition"
|
||||
imagePath="/portfolios/sveske/teachable_machine/thumb.png"
|
||||
/>
|
||||
</Timeline>
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
export class Vector2 {
|
||||
x: number;
|
||||
y: number;
|
||||
|
||||
constructor(x: number, y: number) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
Add(vec2: Vector2) {
|
||||
return new Vector2(this.x + vec2.x, this.y + vec2.y);
|
||||
}
|
||||
|
||||
Sub(vec2: Vector2) {
|
||||
return new Vector2(this.x - vec2.x, this.y - vec2.y);
|
||||
}
|
||||
|
||||
Scale(mult: number) {
|
||||
return new Vector2(this.x * mult, this.y * mult);;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,75 +0,0 @@
|
|||
<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 var(--left-line-color);
|
||||
}
|
||||
|
||||
.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 {
|
||||
|
||||
&: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>
|
||||
|
|
@ -1,66 +0,0 @@
|
|||
<script>
|
||||
import RepeatedSkills from "./RepeatedSkills.svelte";
|
||||
|
||||
// Cedit
|
||||
import LinkToSource from "../CompsRev3/LinkToSource.svelte";
|
||||
|
||||
export let Style = "";
|
||||
export let Class = "";
|
||||
</script>
|
||||
|
||||
<div class="container {Class}" style="{Style}">
|
||||
<div class=" text-center bg-[var(--left-grid-bg-color)]">
|
||||
<RepeatedSkills textOverride={["Hello", "你好", "Hej"]} targetTextHeight={3} targetTextWidth={50} applyRotation={false}/>
|
||||
</div>
|
||||
<div/>
|
||||
<div class="flex justify-center">
|
||||
<div class="w-[6cm]">
|
||||
<LinkToSource/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.container {
|
||||
position: absolute;
|
||||
transform: translate(-30mm, 7.5mm) 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;
|
||||
padding-top: 1mm;
|
||||
|
||||
// Text inside
|
||||
display: grid;
|
||||
place-content: center;
|
||||
border-bottom: var(--left-decor-line-color) dotted 0.5mm;
|
||||
border-top: var(--left-decor-line-color) dotted 0.5mm;
|
||||
|
||||
&:first-child {
|
||||
color: var(--left-decor-text-color);
|
||||
font-size: 3mm;
|
||||
//font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
> div:nth-child(2) {
|
||||
padding-top: 4mm;
|
||||
//border-bottom: #4472c4 dashed 2mm;
|
||||
|
||||
// background-color: var(--left-grid-bg-color);
|
||||
|
||||
background-image: linear-gradient(to right, var(--left-decor-line-color) 70%, rgba(255,255,255,0) 0%);
|
||||
background-position: bottom;
|
||||
background-size: 6mm 1.5mm;
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,76 +0,0 @@
|
|||
<script>
|
||||
import svelteLogo from "$lib/svelteLogos/svelte-logo-cutout.svg"
|
||||
</script>
|
||||
|
||||
<div class="container">
|
||||
<div class="flex justify-center">
|
||||
<div class="corner-border-container p-1 m-1">
|
||||
<div class="flex">
|
||||
This CV was made using html, css and <a class="grid place-content-center" href="https://kit.svelte.dev/"><img src={svelteLogo} class="w-2 h-2" alt="SvelteKit logo"/></a>
|
||||
</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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.corner-border-container {
|
||||
--length: 5px;
|
||||
--width: 1px;
|
||||
background-color: var(--left-grid-bg-color);
|
||||
background-image:
|
||||
linear-gradient(var(--left-decor-line-color), var(--left-decor-line-color)),
|
||||
linear-gradient(var(--left-decor-line-color), var(--left-decor-line-color)),
|
||||
linear-gradient(var(--left-decor-line-color), var(--left-decor-line-color)),
|
||||
linear-gradient(var(--left-decor-line-color), var(--left-decor-line-color)),
|
||||
linear-gradient(var(--left-decor-line-color), var(--left-decor-line-color)),
|
||||
linear-gradient(var(--left-decor-line-color), var(--left-decor-line-color)),
|
||||
linear-gradient(var(--left-decor-line-color), var(--left-decor-line-color)),
|
||||
linear-gradient(var(--left-decor-line-color), var(--left-decor-line-color));
|
||||
background-size:
|
||||
var(--length) var(--width),
|
||||
var(--width) var(--length),
|
||||
var(--length) var(--width),
|
||||
var(--width) var(--length),
|
||||
var(--length) var(--width),
|
||||
var(--width) var(--length),
|
||||
var(--length) var(--width),
|
||||
var(--width) var(--length);
|
||||
background-position:
|
||||
top left,
|
||||
top left,
|
||||
top right,
|
||||
top right,
|
||||
bottom right,
|
||||
bottom right,
|
||||
bottom left,
|
||||
bottom left;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.container {
|
||||
z-index: 1;
|
||||
|
||||
font-size: 0.5rem;
|
||||
|
||||
//white-space: nowrap;
|
||||
|
||||
color: #777777;
|
||||
|
||||
* a {
|
||||
color: #3d6ddc;
|
||||
padding-left: 1mm;
|
||||
padding-right: 1mm;
|
||||
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
|
||||
div:nth-child(2){
|
||||
padding-bottom: 2mm;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
<script>
|
||||
import QRCode from "$lib/zhen/cv-comps/LinkedInQrCode.svg?raw"
|
||||
</script>
|
||||
|
||||
<div class="container">
|
||||
<div>LinkedIn</div>
|
||||
<div class="qrcode corner-border-container p-4">{@html QRCode}</div>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.corner-border-container {
|
||||
--length: 20px;
|
||||
--width: 4px;
|
||||
background-color: var(--left-grid-bg-color);
|
||||
background-image:
|
||||
linear-gradient(var(--left-decor-line-color), var(--left-decor-line-color)),
|
||||
linear-gradient(var(--left-decor-line-color), var(--left-decor-line-color)),
|
||||
linear-gradient(var(--left-decor-line-color), var(--left-decor-line-color)),
|
||||
linear-gradient(var(--left-decor-line-color), var(--left-decor-line-color)),
|
||||
linear-gradient(var(--left-decor-line-color), var(--left-decor-line-color)),
|
||||
linear-gradient(var(--left-decor-line-color), var(--left-decor-line-color)),
|
||||
linear-gradient(var(--left-decor-line-color), var(--left-decor-line-color)),
|
||||
linear-gradient(var(--left-decor-line-color), var(--left-decor-line-color));
|
||||
background-size:
|
||||
var(--length) var(--width),
|
||||
var(--width) var(--length),
|
||||
var(--length) var(--width),
|
||||
var(--width) var(--length),
|
||||
var(--length) var(--width),
|
||||
var(--width) var(--length),
|
||||
var(--length) var(--width),
|
||||
var(--width) var(--length);
|
||||
background-position:
|
||||
top left,
|
||||
top left,
|
||||
top right,
|
||||
top right,
|
||||
bottom right,
|
||||
bottom right,
|
||||
bottom left,
|
||||
bottom left;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
|
||||
.qrcode {
|
||||
transform: scale(0.9);
|
||||
}
|
||||
|
||||
.container {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
|
||||
& * {
|
||||
font-size: 7.5mm;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
<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>
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
<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 var(--left-line-color);
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
<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 var(--left-line-color);
|
||||
}
|
||||
|
||||
.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 {
|
||||
&: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>
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
<div class="short-profile-container">
|
||||
<div class="flex gap-1 items-baseline">
|
||||
<b style="text-align:left;">
|
||||
About me
|
||||
</b>
|
||||
<div class="opacity-70 text-[0.5rem]">
|
||||
I know the languages listed above in the decor!
|
||||
</div>
|
||||
</div>
|
||||
<span>
|
||||
I love learning on my own. I've placed my education at the bottom of my CV.
|
||||
This is because everything I've learnt is on my own, and I refuse to give too much credit to my education.
|
||||
I encurage you to checkout which <span class="font-semibold">Open-source</span> projects I've worked on, on my <a href="https://github.com/MagicBOTAlex"><span class="text-blue-500 underline">github</span></a>.
|
||||
<br>
|
||||
<br>
|
||||
Other than my github, I've listed some projectss highlighted below. :)
|
||||
</span>
|
||||
|
||||
</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>
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
<div class="short-profile-container">
|
||||
<div>
|
||||
<b style="text-align:left;">
|
||||
Short profile
|
||||
</b>
|
||||
</div>
|
||||
<div>
|
||||
◾ Full-stack at Yaaumma <br>
|
||||
◾ "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 var(--left-line-color);
|
||||
}
|
||||
</style>
|
||||
|
|
@ -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>
|
||||
54
src/ts/EnvHandler.ts
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
// used to deal with .env things, but with this instead because nix
|
||||
// Drafted manually, then fed through chatgpt
|
||||
// Idk what is going on anymore, but it isn't really an important aspect of the code, since this is just for changing from productiong to dev environment
|
||||
|
||||
type Env = { [K in keyof typeof env]: (typeof env)[K] } & Record<
|
||||
string,
|
||||
unknown
|
||||
>;
|
||||
|
||||
export let env = {
|
||||
POCKETBASE_URL: "https://pocket.deprived.dev",
|
||||
} as const;
|
||||
export default env;
|
||||
|
||||
let initialized = false;
|
||||
|
||||
// Load overrides from localstorage or url params
|
||||
const isOverride = (k: string) => k.startsWith("-E");
|
||||
const norm = (k: string) => k.slice(2); // strip "-E"
|
||||
|
||||
function apply(from: Record<string, string>, tag: string) {
|
||||
for (const [rawK, rawV] of Object.entries(from)) {
|
||||
env[rawK] = rawV;
|
||||
}
|
||||
}
|
||||
|
||||
export function initEnv(): void {
|
||||
if (initialized) return;
|
||||
initialized = true;
|
||||
window.env = env!;
|
||||
|
||||
// localStorage overrides: only keys starting with "-E"
|
||||
const ls: Record<string, string> = {};
|
||||
for (const [k, v] of Object.entries(env)) {
|
||||
const val = localStorage.getItem(k);
|
||||
if (val != null) ls[k] = val;
|
||||
}
|
||||
apply(ls, "localStorage");
|
||||
|
||||
// URL param overrides: ?-EKEY=value
|
||||
const params = new URLSearchParams(location.search);
|
||||
const url: Record<string, string> = {};
|
||||
for (const [k, v] of params.entries()) url[k] = v;
|
||||
apply(url, "url");
|
||||
|
||||
// (optional) persist URL overrides in localStorage with the "-E" prefix
|
||||
for (const [k, v] of Object.entries(url)) {
|
||||
if (isOverride(k)) localStorage.setItem(norm(k), String(v));
|
||||
}
|
||||
|
||||
// Object.assign(env as any, current);
|
||||
// for (const [k, v] of Object.entries(env))
|
||||
// console.log(`[env final] ${k}=${String(v)}`);
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import { PUBLIC_URL_BASE } from "$env/static/public";
|
||||
const PUBLIC_URL_BASE = "https://deprived.dev/";
|
||||
|
||||
// Absolute vibe coded. Idk if it works or not. Not important anyways
|
||||
// Assumes PUBLIC_URL_BASE is something like "https://deprived.dev"
|
||||
|
|
|
|||
57
src/ts/Redaction/Profile.ts
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
// npm i zod
|
||||
import { z } from "zod";
|
||||
|
||||
const Link = z.object({
|
||||
imageId: z.string().min(1).optional(),
|
||||
text: z.string().min(1),
|
||||
link: z.string().url(),
|
||||
});
|
||||
|
||||
const Experience = z.object({
|
||||
imageId: z.string().min(1),
|
||||
name: z.string().min(1),
|
||||
date: z.string().min(1),
|
||||
});
|
||||
|
||||
const EducationLoose = z.object({
|
||||
imageId: z.string().min(1).optional(),
|
||||
name: z.string().min(1).optional(),
|
||||
});
|
||||
|
||||
const CV = z.object({
|
||||
sourceLink: z.string().url().min(1),
|
||||
});
|
||||
|
||||
const ProfileSchema = z.object({
|
||||
name: z.string().min(1),
|
||||
nick: z.string().min(1),
|
||||
shortProfileHiddenContent: z.array(z.string()),
|
||||
email: z.string().email(),
|
||||
phone: z.string().min(1),
|
||||
linkedIn: Link,
|
||||
itch: Link,
|
||||
cv: CV,
|
||||
experience: z.array(Experience),
|
||||
education: z
|
||||
.array(EducationLoose)
|
||||
.transform((arr) =>
|
||||
arr.filter(
|
||||
(e): e is { imageId: string; name: string } => !!e.imageId && !!e.name,
|
||||
),
|
||||
),
|
||||
});
|
||||
|
||||
export type LinkT = z.infer<typeof Link>;
|
||||
export type ExperienceT = z.infer<typeof Experience>;
|
||||
export type EducationT = { imageId: string; name: string };
|
||||
export type Profile = Omit<z.infer<typeof ProfileSchema>, "education"> & {
|
||||
education: EducationT[];
|
||||
};
|
||||
|
||||
export function parseProfile(json: unknown): Profile {
|
||||
return ProfileSchema.parse(json);
|
||||
}
|
||||
|
||||
// --- usage ---
|
||||
// const raw = JSON.parse(yourJsonString);
|
||||
// const profile = parseProfile(raw);
|
||||
71
src/ts/Redaction/Redactor.ts
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
// redactor.ts
|
||||
import { parseProfile, type Profile } from "./Profile";
|
||||
import env, { initEnv } from "@ts/EnvHandler";
|
||||
import type { Readable, Subscriber, Unsubscriber } from "svelte/store";
|
||||
|
||||
class Redactor implements Readable<Profile | undefined> {
|
||||
public unredactedProfile: Profile | undefined = undefined;
|
||||
public wrongKey: boolean | undefined = undefined;
|
||||
|
||||
private subs = new Set<Subscriber<Profile | undefined>>();
|
||||
subscribe(run: Subscriber<Profile | undefined>): Unsubscriber {
|
||||
this.subs.add(run);
|
||||
run(this.unredactedProfile);
|
||||
return () => this.subs.delete(run);
|
||||
}
|
||||
private notify() {
|
||||
this.subs.forEach((s) => s(this.unredactedProfile));
|
||||
}
|
||||
|
||||
async TryGetUnredacter(): Promise<Profile | string> {
|
||||
if (this.unredactedProfile) return this.unredactedProfile;
|
||||
|
||||
const storedKey = localStorage.getItem("key");
|
||||
if (!storedKey) throw new Error("Missing key");
|
||||
|
||||
const hashResJson = await (
|
||||
await fetch("https://api.deprived.dev/unredact", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ key: storedKey }),
|
||||
})
|
||||
).json();
|
||||
|
||||
const unredactHash = hashResJson.response;
|
||||
initEnv();
|
||||
if (unredactHash == "nawh") {
|
||||
console.log("Wrong key, keeping info about alex secret");
|
||||
return "Wrong key";
|
||||
}
|
||||
|
||||
const url = `${env.POCKETBASE_URL}/api/collections/redacted_content/records/${unredactHash}`;
|
||||
|
||||
const res = await fetch(url, {
|
||||
method: "GET",
|
||||
headers: { Accept: "application/json" },
|
||||
});
|
||||
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
||||
|
||||
let data = await res.json();
|
||||
const replaced = JSON.parse(
|
||||
JSON.stringify(data).replaceAll("[PB]", env.POCKETBASE_URL),
|
||||
);
|
||||
this.unredactedProfile = parseProfile(replaced.json);
|
||||
this.notify(); // <-- tell Svelte to update
|
||||
return "success";
|
||||
}
|
||||
|
||||
t(path: string, fallback: string): string {
|
||||
try {
|
||||
const src = this.unredactedProfile as Record<string, unknown> | undefined;
|
||||
if (!src) return fallback;
|
||||
const v = path.split(".").reduce<any>((o, k) => (o as any)?.[k], src);
|
||||
return (v ?? "") !== "" ? String(v) : fallback;
|
||||
} catch {
|
||||
return fallback;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const re = new Redactor();
|
||||
export default re;
|
||||
|
|
@ -2,8 +2,8 @@
|
|||
// It is meant to be called from stores.ts
|
||||
|
||||
import PocketBase from "pocketbase";
|
||||
import { ShopItem } from "./classes/ShopItem";
|
||||
import { PUBLIC_POCKET_URL, PUBLIC_URL_BASE } from "$env/static/public";
|
||||
import ShopItem from "./classes/ShopItem";
|
||||
const PUBLIC_POCKET_URL = "https://pocket.deprived.dev/";
|
||||
|
||||
export let pb = new PocketBase(PUBLIC_POCKET_URL);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import { PUBLIC_POCKET_URL, PUBLIC_URL_BASE } from "$env/static/public";
|
||||
import { ParseAssetUrl } from "@src/ts/Helper";
|
||||
|
||||
export class ShopItem {
|
||||
|
|
@ -48,3 +47,5 @@ export class ShopItem {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default ShopItem;
|
||||
|
|
|
|||