working gallery, working skills

This commit is contained in:
Aron Petau 2025-05-14 15:08:38 +02:00
parent 9e63f46919
commit 793f028a40
304 changed files with 3751 additions and 200 deletions

View file

@ -63,38 +63,35 @@ auto_hide = false
show_feed = true
show_theme_switcher = true
show_repo = true
links = [
{ name = "Menu", menu = [
{ url = "@/blog/_index.md", name = "Blog" },
{ url = "@/pages/privacy.md", name = "Privacy" },
{ url = "@/pages/cv.md", name = "Experience" },
{ url = "@/pages/about.md", name = "About" },
] },
{ url = "https://kaesewerkstatt.petau.net", name = "Käsewerkstatt" }
{ url = "@/blog/_index.md", name = "Blog" },
{ url = "@/pages/contact.md", name = "Contact Me" },
{ url = "@/pages/cv.md", name = "Experience" },
{ url = "@/pages/about.md", name = "About" },
]
[extra.footer]
links = [
{ url = "@/blog/_index.md", name = "Blog" },
{ url = "@/pages/privacy.md", name = "Privacy" },
{ url = "https://kaesewerkstatt.petau.net", name = "Käsewerkstatt" },
{ url = "https://www.newpractice.net/author/aron-petau", name = "New Practice Network" },
]
# Social links in the footer.
# Any URL-encoded SVG can be used as an icon.
# https://simpleicons.org is the recommended source of SVG icons.
# For URL encoding use https://yoksel.github.io/url-encoder/.
# Make sure that "external quotes" are set to "double".
socials = [
{ url = "https://github.com", name = "GitHub", icon = "%3Csvg role='img' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Ctitle%3EGitHub%3C/title%3E%3Cpath d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12'/%3E%3C/svg%3E" },
{ url = "https://instagram.com", name = "Instagram", icon = "%3Csvg role='img' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Ctitle%3EInstagram%3C/title%3E%3Cpath d='M7.0301.084c-1.2768.0602-2.1487.264-2.911.5634-.7888.3075-1.4575.72-2.1228 1.3877-.6652.6677-1.075 1.3368-1.3802 2.127-.2954.7638-.4956 1.6365-.552 2.914-.0564 1.2775-.0689 1.6882-.0626 4.947.0062 3.2586.0206 3.6671.0825 4.9473.061 1.2765.264 2.1482.5635 2.9107.308.7889.72 1.4573 1.388 2.1228.6679.6655 1.3365 1.0743 2.1285 1.38.7632.295 1.6361.4961 2.9134.552 1.2773.056 1.6884.069 4.9462.0627 3.2578-.0062 3.668-.0207 4.9478-.0814 1.28-.0607 2.147-.2652 2.9098-.5633.7889-.3086 1.4578-.72 2.1228-1.3881.665-.6682 1.0745-1.3378 1.3795-2.1284.2957-.7632.4966-1.636.552-2.9124.056-1.2809.0692-1.6898.063-4.948-.0063-3.2583-.021-3.6668-.0817-4.9465-.0607-1.2797-.264-2.1487-.5633-2.9117-.3084-.7889-.72-1.4568-1.3876-2.1228C21.2982 1.33 20.628.9208 19.8378.6165 19.074.321 18.2017.1197 16.9244.0645 15.6471.0093 15.236-.005 11.977.0014 8.718.0076 8.31.0215 7.0301.0839m.1402 21.6932c-1.17-.0509-1.8053-.2453-2.2287-.408-.5606-.216-.96-.4771-1.3819-.895-.422-.4178-.6811-.8186-.9-1.378-.1644-.4234-.3624-1.058-.4171-2.228-.0595-1.2645-.072-1.6442-.079-4.848-.007-3.2037.0053-3.583.0607-4.848.05-1.169.2456-1.805.408-2.2282.216-.5613.4762-.96.895-1.3816.4188-.4217.8184-.6814 1.3783-.9003.423-.1651 1.0575-.3614 2.227-.4171 1.2655-.06 1.6447-.072 4.848-.079 3.2033-.007 3.5835.005 4.8495.0608 1.169.0508 1.8053.2445 2.228.408.5608.216.96.4754 1.3816.895.4217.4194.6816.8176.9005 1.3787.1653.4217.3617 1.056.4169 2.2263.0602 1.2655.0739 1.645.0796 4.848.0058 3.203-.0055 3.5834-.061 4.848-.051 1.17-.245 1.8055-.408 2.2294-.216.5604-.4763.96-.8954 1.3814-.419.4215-.8181.6811-1.3783.9-.4224.1649-1.0577.3617-2.2262.4174-1.2656.0595-1.6448.072-4.8493.079-3.2045.007-3.5825-.006-4.848-.0608M16.953 5.5864A1.44 1.44 0 1 0 18.39 4.144a1.44 1.44 0 0 0-1.437 1.4424M5.8385 12.012c.0067 3.4032 2.7706 6.1557 6.173 6.1493 3.4026-.0065 6.157-2.7701 6.1506-6.1733-.0065-3.4032-2.771-6.1565-6.174-6.1498-3.403.0067-6.156 2.771-6.1496 6.1738M8 12.0077a4 4 0 1 1 4.008 3.9921A3.9996 3.9996 0 0 1 8 12.0077'/%3E%3C/svg%3E" },
{ url = "https://mastodon.social", name = "Mastodon", icon = "%3Csvg role='img' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Ctitle%3EMastodon%3C/title%3E%3Cpath d='M23.268 5.313c-.35-2.578-2.617-4.61-5.304-5.004C17.51.242 15.792 0 11.813 0h-.03c-3.98 0-4.835.242-5.288.309C3.882.692 1.496 2.518.917 5.127.64 6.412.61 7.837.661 9.143c.074 1.874.088 3.745.26 5.611.118 1.24.325 2.47.62 3.68.55 2.237 2.777 4.098 4.96 4.857 2.336.792 4.849.923 7.256.38.265-.061.527-.132.786-.213.585-.184 1.27-.39 1.774-.753a.057.057 0 0 0 .023-.043v-1.809a.052.052 0 0 0-.02-.041.053.053 0 0 0-.046-.01 20.282 20.282 0 0 1-4.709.545c-2.73 0-3.463-1.284-3.674-1.818a5.593 5.593 0 0 1-.319-1.433.053.053 0 0 1 .066-.054c1.517.363 3.072.546 4.632.546.376 0 .75 0 1.125-.01 1.57-.044 3.224-.124 4.768-.422.038-.008.077-.015.11-.024 2.435-.464 4.753-1.92 4.989-5.604.008-.145.03-1.52.03-1.67.002-.512.167-3.63-.024-5.545zm-3.748 9.195h-2.561V8.29c0-1.309-.55-1.976-1.67-1.976-1.23 0-1.846.79-1.846 2.35v3.403h-2.546V8.663c0-1.56-.617-2.35-1.848-2.35-1.112 0-1.668.668-1.67 1.977v6.218H4.822V8.102c0-1.31.337-2.35 1.011-3.12.696-.77 1.608-1.164 2.74-1.164 1.311 0 2.302.5 2.962 1.498l.638 1.06.638-1.06c.66-.999 1.65-1.498 2.96-1.498 1.13 0 2.043.395 2.74 1.164.675.77 1.012 1.81 1.012 3.12z'/%3E%3C/svg%3E" },
{ url = "https://github.com/arontaupe", name = "GitHub", icon = "%3Csvg role='img' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Ctitle%3EGitHub%3C/title%3E%3Cpath d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12'/%3E%3C/svg%3E" },
{url = "https://www.printables.com/@arontaupe", name = "Printables",icon = "%3Csvg%20role%3D%22img%22%20viewBox%3D%220%200%2024%2024%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Ctitle%3EPrintables%3C%2Ftitle%3E%3Cpath%20d%3D%22M3.678%204.8%2012%209.6v9.6l8.322-4.8V4.8L12%200ZM12%2019.2l-8.322-4.8V24Z%22%2F%3E%3C%2Fsvg%3E"},
{url = "https://www.etsy.com/de-en/shop/reprintedservices", name = "Etsy", icon = "%3Csvg%20role%3D%22img%22%20viewBox%3D%220%200%2024%2024%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Ctitle%3EEtsy%3C%2Ftitle%3E%3Cpath%20d%3D%22M8.559%202.445c0-.325.033-.52.59-.52h7.465c1.3%200%202.02%201.11%202.54%203.193l.42%201.666h1.27c.23-4.728.43-6.784.43-6.784s-3.196.36-5.09.36H6.635L1.521.196v1.37l1.725.326c1.21.24%201.5.496%201.6%201.606%200%200%20.11%203.27.11%208.64%200%205.385-.09%208.61-.09%208.61%200%20.973-.39%201.333-1.59%201.573l-1.722.33V24l5.13-.165h8.55c1.935%200%206.39.165%206.39.165.105-1.17.75-6.48.855-7.064h-1.2l-1.284%202.91c-1.005%202.28-2.476%202.445-4.11%202.445h-4.906c-1.63%200-2.415-.64-2.415-2.05V12.8s3.62%200%204.79.096c.912.064%201.463.325%201.76%201.598l.39%201.695h1.41l-.09-4.278.192-4.305h-1.391l-.45%201.89c-.283%201.244-.48%201.47-1.754%201.6-1.666.17-4.815.14-4.815.14V2.45h-.05z%22%2F%3E%3C%2Fsvg%3E"},
{ url = "https://mastodon.online/@reprintedAron", name = "Mastodon", icon = "%3Csvg role='img' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Ctitle%3EMastodon%3C/title%3E%3Cpath d='M23.268 5.313c-.35-2.578-2.617-4.61-5.304-5.004C17.51.242 15.792 0 11.813 0h-.03c-3.98 0-4.835.242-5.288.309C3.882.692 1.496 2.518.917 5.127.64 6.412.61 7.837.661 9.143c.074 1.874.088 3.745.26 5.611.118 1.24.325 2.47.62 3.68.55 2.237 2.777 4.098 4.96 4.857 2.336.792 4.849.923 7.256.38.265-.061.527-.132.786-.213.585-.184 1.27-.39 1.774-.753a.057.057 0 0 0 .023-.043v-1.809a.052.052 0 0 0-.02-.041.053.053 0 0 0-.046-.01 20.282 20.282 0 0 1-4.709.545c-2.73 0-3.463-1.284-3.674-1.818a5.593 5.593 0 0 1-.319-1.433.053.053 0 0 1 .066-.054c1.517.363 3.072.546 4.632.546.376 0 .75 0 1.125-.01 1.57-.044 3.224-.124 4.768-.422.038-.008.077-.015.11-.024 2.435-.464 4.753-1.92 4.989-5.604.008-.145.03-1.52.03-1.67.002-.512.167-3.63-.024-5.545zm-3.748 9.195h-2.561V8.29c0-1.309-.55-1.976-1.67-1.976-1.23 0-1.846.79-1.846 2.35v3.403h-2.546V8.663c0-1.56-.617-2.35-1.848-2.35-1.112 0-1.668.668-1.67 1.977v6.218H4.822V8.102c0-1.31.337-2.35 1.011-3.12.696-.77 1.608-1.164 2.74-1.164 1.311 0 2.302.5 2.962 1.498l.638 1.06.638-1.06c.66-.999 1.65-1.498 2.96-1.498 1.13 0 2.043.395 2.74 1.164.675.77 1.012 1.81 1.012 3.12z'/%3E%3C/svg%3E" },
]
show_copyright = true
show_powered_by = false
# Whether to show link to website source
show_source = false
#copyright = "© *Aperture* **Science** ~~Innovators~~, `1972`"
# Based on https://carlschwan.eu/2020/12/29/adding-comments-to-your-static-blog-with-mastodon/
#
@ -106,9 +103,9 @@ show_source = false
# the needed rel="me" link is set in the head based on these.
[extra.comments]
# Your Mastodon API host; instance that you have an account on.
host = "vmst.io"
host = "mastodon.online"
# Your Mastodon username; used to determine who the original poster is.
user = "daudix"
user = "reprintedAron"
# Whether to show the QR code to Mastodon post
show_qr = true

View file

@ -9,7 +9,7 @@ show_shares = true
+++
# Welcome
## Welcome
to the online presence of Aron Petau.
@ -17,8 +17,9 @@ This site is a collection of my thoughts and experiences.
I hope you find something interesting here.
This Page is currently under construction.
{% alert(note=true) %}
This Page is currently under active construction.
Broken links are to be expected.
{% end %}
<progress value="25" max="100"></progress>
<progress value="75" max="100"></progress>

View file

Before

Width:  |  Height:  |  Size: 139 KiB

After

Width:  |  Height:  |  Size: 139 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 672 KiB

After

Width:  |  Height:  |  Size: 672 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 880 KiB

After

Width:  |  Height:  |  Size: 880 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 247 KiB

After

Width:  |  Height:  |  Size: 247 KiB

Before After
Before After

View file

@ -32,7 +32,7 @@ show_shares = true
+++
```json
{% gallery() %}
[
{
@ -85,7 +85,8 @@ show_shares = true
"title": "A custom-built printer enclosure made up of 3 Ikea Lack tables and around 3 kgs of plastic."
}
]
```
{% end %}
## 3D Printing

View file

Before

Width:  |  Height:  |  Size: 413 KiB

After

Width:  |  Height:  |  Size: 413 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 506 KiB

After

Width:  |  Height:  |  Size: 506 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 536 KiB

After

Width:  |  Height:  |  Size: 536 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 655 KiB

After

Width:  |  Height:  |  Size: 655 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 110 KiB

After

Width:  |  Height:  |  Size: 110 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 1 MiB

After

Width:  |  Height:  |  Size: 1 MiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 641 KiB

After

Width:  |  Height:  |  Size: 641 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 783 KiB

After

Width:  |  Height:  |  Size: 783 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 356 KiB

After

Width:  |  Height:  |  Size: 356 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 1.3 MiB

Before After
Before After

View file

@ -20,30 +20,9 @@ tags = [
"yeast"
]
gallery:
- url: /assets/images/beer_setup.jpg
image_path: /assets/images/beer_setup.jpg
title: "The latest iteration of my homebrew setup, using pressure tanks and a pressurized fermentation chamber"
- url: /assets/images/beer_setup_2.jpg
image_path: /assets/images/beer_setup_2.jpg
title: "An electric kettle I use for the Brew"
- url: /assets/images/beer_tap.jpg
image_path: /assets/images/beer_tap.jpg
title: "I made my own kegging system featuring a tap from an old table leg."
- url: /assets/images/beer_fermentation.jpg
image_path: /assets/images/beer_fermentation.jpg
title: "An active fermentation"
- url: /assets/images/hops.jpg
image_path: /assets/images/hops.jpg
title: "Hops growing in our garden, so I can experiment with fresh specialty hops"
- url: /assets/images/beer_malt.jpg
image_path: /assets/images/beer_malt.jpg
title: "The leftover mass of spent grain.
Animals love it, it's great for composting,
but most importantly, its great for baking bread!"
[extra]
banner = "/images/beer_tap.jpg"
banner = "beer_tap.jpg"
show_copyright = true
show_shares = true
+++
@ -61,8 +40,41 @@ Yeast and what it does fascinates me. Every time I open the latch to release som
I am not yet an expert on algae, but I can manage with yeast and I believe they can coexist and create a more sustainable cycle of production.
Young Henrys, a brewery in Australia is already incorporating algae into its industrial process:
[The Algae project](https://younghenrys.com/algae){: .btn .btn--large}
[The Algae project](https://younghenrys.com/algae)
Such ideas do not come into the industry by themselves: I believe that art and the exploratory discovery of novel techniques are the same things. Good and inventive design can improve society and make steps towards sustainability. I want to be part of that and would love to find new ways of using yeast in other design contexts: See whether I can make them work in a closed circular system, make them calculate things for me, or simply making my next beer taste awesome with just the right amount of fizz.
{% include gallery caption="Some selected photos of the process in our Kitchen" %}
{% gallery() %}
[
{
"file": "beer_setup.jpg",
"title": "The latest iteration of my homebrew setup, using pressure tanks and a pressurized fermentation chamber",
"alt": "A photo showcasing the latest version of a homebrew setup with pressure tanks and a fermentation chamber."
},
{
"file": "beer_setup_2.jpg",
"title": "An electric kettle I use for the Brew",
"alt": "An image of the electric kettle used in the brewing process."
},
{
"file": "beer_tap.jpg",
"title": "I made my own kegging system featuring a tap from an old table leg.",
"alt": "A custom kegging system with a tap made from an upcycled table leg."
},
{
"file": "beer_fermentation.jpg",
"title": "An active fermentation",
"alt": "A photo showing the fermentation process in action during beer brewing."
},
{
"file": "hops.jpg",
"title": "Hops growing in our garden, so I can experiment with fresh specialty hops",
"alt": "Fresh hops growing in the garden, ready for experimentation in brewing."
},
{
"file": "beer_malt.jpg",
"title": "The leftover mass of spent grain. Animals love it, it's great for composting, but most importantly, it's great for baking bread!",
"alt": "The spent grain left over from brewing, which is perfect for composting, animal feed, or baking bread."
}
]
{% end %}

View file

@ -1,30 +1,9 @@
+++
title = "Coding Examples"
date = 2022-03-01
date = 2021-03-01
authors = ["Aron Petau"]
description = "A selection of coding projects from my Bachelor's in Cognitive Science"
gallery:
- url: /assets/images/sample_lr.png
image_path: /assets/images/sample_lr.png
title: "A low-resolution sample"
- url: /assets/images/sample_hr.png
image_path: /assets/images/sample_hr.png
alt: ""
title: "A high-resolution sample. This is also called 'ground truth' "
- url: /assets/images/sample_sr.png
image_path: /assets/images/sample_sr.png
alt: " "
title: "The artificially enlarged image patch resulting from the algorithm"
- url: /assets/images/sample_loss.png
image_path: /assets/images/sample_loss.png
alt: ""
title: "A graph showing an exemplary loss function applied during training"
- url: /assets/images/sample_cos_sim.png
image_path: /assets/images/sample_cos_sim.png
alt: ""
title: "One qualitative measurement we used was pixel-wise cosine similarity. It is used to measure how similar the output and the ground truth images are"
[taxonomies]
tags = [
"AI",
@ -47,7 +26,7 @@ tags = [
]
[extra]
banner = "/images/sample_lr.png"
banner = "sample_lr.png"
show_copyright = true
show_shares = true
+++
@ -63,16 +42,48 @@ Although pure coding and debugging are often not a passion of mine, I recognize
Image Super-Resolution is a hugely important topic in Computer Vision. If it works sufficiently advanced, we could take all our screenshots and selfies and cat pictures from the 2006 facebook-era and even from before and scale them up to suit modern 4K needs.
Just to give an example of what is possible in 2020, just 4 years after the paper here, have a look at this video from 1902:
{% include video id="EQs5VxNPhzk" provider="youtube" %}
{{ youtube(id="EQs5VxNPhzk") }}
The 2016 paper we had a look at is much more modest: it tries to upscale only a single Image, but historically, it was one of the first to achieve computing times sufficiently small to make such realtime-video-upscaling as visible in the Video (from 2020) or of the likes that Nvidia uses nowadays to upscale Videogames.
{% include gallery caption="Example of a Super-Resolution Image. The Neural network is artificially adding Pixels so that we can finally put our measly selfie on a billboard poster and not be appalled by our deformed-and-pixelated-through-technology face." %}
Example of a Super-Resolution Image.
The Neural network is artificially adding Pixels so that we can finally put our measly selfie on a billboard poster and not be appalled by our deformed-and-pixelated-through-technology face.
[The Python notebook for Image super-resolution in Colab]( https://colab.research.google.com/drive/1RlgIKJmX8Omz9CTktX7cdIV_BwarUFpv?usp=sharing){: .btn .btn--large}
{% gallery() %}
[
{
"file": "sample_lr.png",
"title": "A low-resolution sample",
"alt": "A sample image with low resolution, used as a baseline for comparison."
},
{
"file": "sample_hr.png",
"title": "A high-resolution sample. This is also called 'ground truth'",
"alt": "A high-resolution image that serves as the reference ground truth for comparison with other samples."
},
{
"file": "sample_sr.png",
"title": "The artificially enlarged image patch resulting from the algorithm",
"alt": "A sample image where the resolution has been artificially increased using an image enhancement algorithm."
},
{
"file": "sample_loss.png",
"title": "A graph showing an exemplary loss function applied during training",
"alt": "A graph illustrating the loss function used to train the model, showing the model's performance over time."
},
{
"file": "sample_cos_sim.png",
"title": "One qualitative measurement we used was pixel-wise cosine similarity. It is used to measure how similar the output and the ground truth images are",
"alt": "A visualization of pixel-wise cosine similarity, used to quantify how similar the generated image is to the ground truth image."
}
]
{% end %}
[The Python notebook for Image super-resolution in Colab]( https://colab.research.google.com/drive/1RlgIKJmX8Omz9CTktX7cdIV_BwarUFpv?usp=sharing)
### MTCNN (Application and Comparison of a 2016 Paper)
Here, you can also have a look at another, much smaller project, where we rebuilt a rather classical Machine learning approach for face detection. Here, we use preexisting libraries to demonstrate the difference in efficacy of approaches, showing that Multi-task Cascaded Convolutional Networks (MTCNN) was one of the best-performing approaches in 2016. Since I invested much more love and work into the above project, I would prefer for you to check that one out, in case two projects are too much.
[Face detection using a classical AI Approach (Recreation of a 2016 Paper)](https://colab.research.google.com/drive/1uNGsVZ0Q42JRNa3BuI4W-JNJHaXD26bu?usp=sharing){: .btn .btn--large}
[Face detection using a classical AI Approach (Recreation of a 2016 Paper)](https://colab.research.google.com/drive/1uNGsVZ0Q42JRNa3BuI4W-JNJHaXD26bu?usp=sharing)

View file

Before

Width:  |  Height:  |  Size: 5.8 KiB

After

Width:  |  Height:  |  Size: 5.8 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

Before After
Before After

View file

@ -58,18 +58,22 @@ on waste as both a challenge and an opportunity for sustainable futures and a ma
reality of the human experience.
<embed
src="/assets/documents/Human_Waste_MA_Aron_Petau.pdf"
src="/documents/Human_Waste_MA_Aron_Petau.pdf"
type="application/pdf"
style="width: 100%; height: 80vh; margin: 0 auto; display: block; border: 1px solid #ccc;" />
style="width: 100%; height: 80vh; margin: 0 auto; display: block; border: 1px solid #ccc;"
/>
<div class="buttons">
<a class="colored external" href="https://pinry.petau.net">See the image archive yourself</a>
<div class="buttons centered">
<a class="big colored external"
href="https://pinry.petau.net">See the image archive yourself</a>
</div>
<div class="buttons">
<a class="colored external" href="https://archive.petau.net/#/graph">See the archive graph yourself</a>
<div class="buttons centered">
<a class="big colored external"
href="https://archive.petau.net/#/graph">See the archive graph yourself</a>
</div>
<div class="buttons">
<a class="colored external" href="https://forgejo.petau.net/aron/machine_archivist.git">Find the complete Repo on Forgejo</a>
<div class="buttons centered">
<a class="big colored external"
href="https://forgejo.petau.net/aron/machine_archivist.git">Find the complete Repo on Forgejo</a>
</div>

View file

@ -6,24 +6,9 @@ date= 2025-05-01
+++
For starters, mails are gold and probably still the best way to reach me.
[contact me](/mailto:aron@petau.net/)
[contact me](mailto:aron@petau.net)
<div class="buttons">
<a class="colored external" href="mailto:aron@petau.net">Email</a>
</div>
<div class="buttons">
<a class="colored external" href="https://t.me/apetau">Telegram</a>
</div>
<div class="buttons">
<a class="colored external" href="https://github.com/arontaupe">GitHub</a>
</div>
<div class="buttons">
<a class="colored external" href="https://www.printables.com/social/97957-arontaupe/models">Printables</a>
</div>
<div class="buttons">
<a class="colored external" href="https://mastodon.online/@reprintedAron">Mastodon</a>
</div>
<div class="buttons">
<a class="colored external" href="https://www.newpractice.net/author/aron-petau">New Practice Network</a>
</div>
</div>

View file

@ -2,6 +2,7 @@
title = "Curriculum vitae"
description = "Aron writes about their past experience"
authors = ["Aron Petau"]
toc = true
+++
## Who am I?
@ -148,50 +149,189 @@ Contact me via [Email](mailto:aron@petau.net) for further questions.
]
{% end %}
{% import "macros/skills.html" as skills %}
<div>
{{ skills::display() | safe }}
</div>
## Software Skills
{% skills() %}
[
{
"name": "UNIX",
"skills": [
{ "name": "bash", "icon": "fas fa-terminal", "link": "https://www.gnu.org/software/bash/" },
{ "name": "zsh", "icon": "fas fa-terminal", "link": "https://www.zsh.org/" },
{ "name": "Arduino IDE", "icon": "fas fa-cogs", "link": "https://www.arduino.cc/en/software" },
{ "name": "Raspberry Pi", "icon": "fab fa-raspberry-pi", "link": "https://www.raspberrypi.org/" }
]
},
{
"name": "Python",
"skills": [
{ "name": "scipy", "icon": "fab fa-python", "link": "https://scipy.org/" },
{ "name": "matplotlib", "icon": "fab fa-python", "link": "https://matplotlib.org/" },
{ "name": "seaborn", "icon": "fab fa-python", "link": "https://seaborn.pydata.org/" },
{ "name": "pandas", "icon": "fab fa-python", "link": "https://pandas.pydata.org/" },
{ "name": "jupyter notebook", "icon": "fab fa-python", "link": "https://jupyter.org/" },
{ "name": "tensorflow", "icon": "fab fa-python", "link": "https://www.tensorflow.org/" },
{ "name": "pytorch", "icon": "fab fa-python", "link": "https://pytorch.org/" },
{ "name": "scikit-learn", "icon": "fab fa-python", "link": "https://scikit-learn.org/" },
{ "name": "opencv", "icon": "fab fa-python", "link": "https://opencv.org/" },
{ "name": "flask", "icon": "fab fa-python", "link": "https://flask.palletsprojects.com/" },
{ "name": "micropython", "icon": "fab fa-python", "link": "https://micropython.org/" },
{ "name": "circuitpython", "icon": "fab fa-python", "link": "https://circuitpython.org/" }
]
},
{
"name": "HTML, CSS, JavaScript",
"skills": [
{ "name": "HTML", "icon": "fab fa-html5", "link": "https://developer.mozilla.org/en-US/docs/Web/HTML" },
{ "name": "CSS", "icon": "fab fa-css3-alt", "link": "https://developer.mozilla.org/en-US/docs/Web/CSS" },
{ "name": "JavaScript", "icon": "fab fa-js", "link": "https://developer.mozilla.org/en-US/docs/Web/JavaScript" },
{ "name": "GH-pages", "icon": "fab fa-github", "link": "https://pages.github.com/" },
{ "name": "psychoJS", "icon": "fab fa-python", "link": "https://www.psychopy.org/psychojs/" },
{ "name": "AMPStack", "icon": "fab fa-github", "link": "https://github.com/ampstack/ampstack" },
{ "name": "ibex-farm", "icon": "fab fa-github", "link": "https://www.labvanced.com/" }
]
},
{
"name": "C#",
"skills": [
{ "name": "C#", "icon": "fab fa-microsoft", "link": "https://learn.microsoft.com/en-us/dotnet/csharp/" },
{ "name": "Visual Studio", "icon": "fab fa-microsoft", "link": "https://visualstudio.microsoft.com/" },
{ "name": "Unity", "icon": "fab fa-unity", "link": "https://unity.com/" },
{ "name": "Unreal Engine", "icon": "fab fa-unreal-engine", "link": "https://www.unrealengine.com/" }
]
},
### Software Skills
{
"name": "CAD",
"skills": [
{ "name": "Fusion 360", "icon": "fas fa-cogs", "link": "https://www.autodesk.com/products/fusion-360/overview" },
{ "name": "TinkerCAD", "icon": "fas fa-cogs", "link": "https://www.tinkercad.com/" },
{ "name": "Rhino/Grasshopper", "icon": "fas fa-cogs", "link": "https://www.rhino3d.com/" }
]
},
{
"name": "Slicing",
"skills": [
{ "name": "PrusaSlicer", "icon": "fab fa-github", "link": "https://www.prusa3d.com/prusaslicer/" },
{ "name": "kiri.moto", "icon": "fab fa-github", "link": "https://kiri.moto.github.io/" },
{ "name": "Cura", "icon": "fab fa-github", "link": "https://ultimaker.com/software/ultimaker-cura" },
{ "name": "Lightburn", "icon": "fab fa-github", "link": "https://lightburnsoftware.com/" }
]
},
{
"name": "Images",
"skills": [
{ "name": "Inkscape", "icon": "fab fa-github", "link": "https://inkscape.org/" },
{ "name": "Illustrator", "icon": "fab fa-adobe", "link": "https://www.adobe.com/products/illustrator.html" },
{ "name": "GIMP", "icon": "fab fa-github", "link": "https://www.gimp.org/" },
{ "name": "Photoshop", "icon": "fab fa-adobe", "link": "https://www.adobe.com/products/photoshop.html" },
{ "name": "Lightroom", "icon": "fab fa-adobe", "link": "https://www.adobe.com/products/photoshop-lightroom.html" },
{ "name": "Agisoft Metashape", "icon": "fab fa-github", "link": "https://www.agisoft.com/" }
]
},
{
"name": "Video",
"skills": [
{ "name": "Premiere", "icon": "fab fa-adobe", "link": "https://www.adobe.com/products/premiere.html" },
{ "name": "DaVinci Resolve", "icon": "fas fa-film", "link": "https://www.blackmagicdesign.com/products/davinciresolve/" },
{ "name": "OBS", "icon": "fab fa-github", "link": "https://obsproject.com/" },
{ "name": "TouchDesigner", "icon": "fab fa-github", "link": "https://derivative.ca/" },
{ "name": "DJI Drones", "icon": "fas fa-camera", "link": "https://www.dji.com/" },
{ "name": "FPV Drones", "icon": "fas fa-camera", "link": "https://www.fpvdrone.com/" }
]
},
{
"name": "Audio",
"skills": [
{ "name": "Audacity", "icon": "fab fa-github", "link": "https://www.audacityteam.org/" },
{ "name": "supercollider", "icon": "fab fa-github", "link": "https://supercollider.github.io/" }
]
},
{
"name": "Office",
"skills": [
{ "name": "MS-Office Suite", "icon": "fab fa-microsoft", "link": "https://www.microsoft.com/en-us/microsoft-365" },
{ "name": "Latex", "icon": "fab fa-latex", "link": "https://www.latex-project.org/" },
{ "name": "Markdown", "icon": "fab fa-markdown", "link": "https://www.markdownguide.org/" },
{ "name": "Typst", "icon": "fab fa-github", "link": "https://typst.app/" }
]
},
{
"name": "Educational",
"skills": [
{ "name": "Scratch", "icon": "fab fa-scratch", "link": "https://scratch.mit.edu/" },
{ "name": "Makey-Makey", "icon": "fab fa-github", "link": "https://www.makeymakey.com/" },
{ "name": "Ozobot", "icon": "fab fa-github", "link": "https://ozobot.com/" },
{ "name": "Cospaces", "icon": "fab fa-github", "link": "https://cospaces.io/" }
]
}
]
{% end %}
**UNIX** \| bash / zsh \| Arduino IDE \| Raspberry Pi
## Machine Skills
**Python** \| scipy \| matplotlib \| seaborn \| pandas \| jupyter notebook \| tensorflow \| pytorch \| scikit-learn \| opencv \| flask \| micropython \| circuitpython
**HTML, CSS, JavaScript** \| GH-pages \| psychoJS \| AMPStack \| ibex-farm
**C#** \| Unity \| Unreal Engine \| Visual Studio
**CAD** \| Fusion 360 \| TinkerCAD \| Rhino/Grasshopper
**Slicing** \| PrusaSlicer \| kiri.moto \| Cura \| Lightburn
**Images** \| Inkscape \| Illustrator \| GIMP \| Photoshop \| Lightroom \| Agisoft Metashape
**Video** \| Premiere \| DaVinci Resolve \| OBS \| TouchDesigner \| DJI Drones \| FPV Drones
**Audio** \| Audacity \| supercollider
**Office** \| MS-Office Suite \| Latex \| Markdown \| Typst
**Educational** \| Scratch \| Makey-Makey \| Ozobot \| Cospaces
### Machine Skills
**3D Printer** \| Fused-Deposition Modeling (FDM)\| Resin (MSLA)
**CNC** \| Nomad 3 \| Shaper Origin
**(Laser)cutter** \| Cricut \| Mr. Beam \| BRM Lasers Pro 1600 \| X-Tool S1 \| Scissors
**Textile** \| Brother Innov-is V3 Stitching Robot \| Sewing Machine \| Handstitching
**VR** \| Meta Quest 2 \| HTC Vive Pro
**AR** \| Magic Leap 1 \| Apple AR Suite
**Microcontroller** \| Arduino \| Raspberry Pi \| ESP32 \| Pi Pico
**Industrial robots** \| Universal Robots UR5
{% skills() %}
[
{
"name": "3D Printer",
"skills": [
{ "name": "Fused-Deposition Modeling (FDM)", "icon": "fas fa-cube", "link": "https://en.wikipedia.org/wiki/Fused_filament_fabrication" },
{ "name": "Resin (MSLA)", "icon": "fas fa-cube", "link": "https://en.wikipedia.org/wiki/Stereolithography" }
]
},
{
"name": "CNC",
"skills": [
{ "name": "Nomad 3", "icon": "fas fa-tools", "link": "https://shop.carbide3d.com/products/nomad-3" },
{ "name": "Shaper Origin", "icon": "fas fa-tools", "link": "https://www.shapertools.com/" }
]
},
{
"name": "Lasercutting",
"skills": [
{ "name": "Cricut", "icon": "fas fa-cut", "link": "https://cricut.com/" },
{ "name": "Mr. Beam", "icon": "fas fa-bolt", "link": "https://www.mr-beam.org/" },
{ "name": "BRM Lasers Pro 1600", "icon": "fas fa-bolt", "link": "https://www.brmlasers.com/products/pro-1600/" },
{ "name": "X-Tool S1", "icon": "fas fa-bolt", "link": "https://www.xtool.com/products/xtool-s1-laser-cutter" },
{ "name": "Scissors", "icon": "fas fa-scissors" }
]
},
{
"name": "Textile",
"skills": [
{ "name": "Brother Innov-is V3 Stitching Robot", "icon": "fas fa-tshirt", "link": "https://www.brothersewing.co.uk/products/innov-is-v3" },
{ "name": "Sewing Machine", "icon": "fas fa-tshirt" },
{ "name": "Handstitching", "icon": "fas fa-hand-paper" }
]
},
{
"name": "VR",
"skills": [
{ "name": "Meta Quest 2", "icon": "fas fa-vr-cardboard", "link": "https://www.meta.com/quest/quest-2/" },
{ "name": "HTC Vive Pro", "icon": "fas fa-vr-cardboard", "link": "https://www.vive.com/eu/product/vive-pro/" }
]
},
{
"name": "AR",
"skills": [
{ "name": "Magic Leap 1", "icon": "fas fa-glasses", "link": "https://www.magicleap.com/magic-leap-1" },
{ "name": "Apple AR Suite", "icon": "fab fa-apple", "link": "https://developer.apple.com/augmented-reality/" }
]
},
{
"name": "Microcontroller",
"skills": [
{ "name": "Arduino", "icon": "fas fa-microchip", "link": "https://www.arduino.cc/" },
{ "name": "Raspberry Pi", "icon": "fab fa-raspberry-pi", "link": "https://www.raspberrypi.com/" },
{ "name": "ESP32", "icon": "fas fa-microchip", "link": "https://www.espressif.com/en/products/socs/esp32" },
{ "name": "Pi Pico", "icon": "fas fa-microchip", "link": "https://www.raspberrypi.com/products/raspberry-pi-pico/" }
]
},
{
"name": "Industrial robots",
"skills": [
{ "name": "Universal Robots UR5", "icon": "fas fa-robot", "link": "https://www.universal-robots.com/products/ur5-robot/" }
]
}
]
{% end %}

BIN
public/404.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4 KiB

BIN
public/404.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 340 B

BIN
public/apple-touch-icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

1
public/auto-render.min.js vendored Normal file
View file

@ -0,0 +1 @@
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("katex")):"function"==typeof define&&define.amd?define(["katex"],t):"object"==typeof exports?exports.renderMathInElement=t(require("katex")):e.renderMathInElement=t(e.katex)}("undefined"!=typeof self?self:this,(function(e){return function(){"use strict";var t={771:function(t){t.exports=e}},n={};function r(e){var o=n[e];if(void 0!==o)return o.exports;var i=n[e]={exports:{}};return t[e](i,i.exports,r),i.exports}r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,{a:t}),t},r.d=function(e,t){for(var n in t)r.o(t,n)&&!r.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)};var o={};return function(){r.d(o,{default:function(){return d}});var e=r(771),t=r.n(e);const n=function(e,t,n){let r=n,o=0;const i=e.length;for(;r<t.length;){const n=t[r];if(o<=0&&t.slice(r,r+i)===e)return r;"\\"===n?r++:"{"===n?o++:"}"===n&&o--,r++}return-1},i=/^\\begin{/;var a=function(e,t){let r;const o=[],a=new RegExp("("+t.map((e=>e.left.replace(/[-/\\^$*+?.()|[\]{}]/g,"\\$&"))).join("|")+")");for(;r=e.search(a),-1!==r;){r>0&&(o.push({type:"text",data:e.slice(0,r)}),e=e.slice(r));const a=t.findIndex((t=>e.startsWith(t.left)));if(r=n(t[a].right,e,t[a].left.length),-1===r)break;const l=e.slice(0,r+t[a].right.length),s=i.test(l)?l:e.slice(t[a].left.length,r);o.push({type:"math",data:s,rawData:l,display:t[a].display}),e=e.slice(r+t[a].right.length)}return""!==e&&o.push({type:"text",data:e}),o};const l=function(e,n){const r=a(e,n.delimiters);if(1===r.length&&"text"===r[0].type)return null;const o=document.createDocumentFragment();for(let e=0;e<r.length;e++)if("text"===r[e].type)o.appendChild(document.createTextNode(r[e].data));else{const i=document.createElement("span");let a=r[e].data;n.displayMode=r[e].display;try{n.preProcess&&(a=n.preProcess(a)),t().render(a,i,n)}catch(i){if(!(i instanceof t().ParseError))throw i;n.errorCallback("KaTeX auto-render: Failed to parse `"+r[e].data+"` with ",i),o.appendChild(document.createTextNode(r[e].rawData));continue}o.appendChild(i)}return o},s=function(e,t){for(let n=0;n<e.childNodes.length;n++){const r=e.childNodes[n];if(3===r.nodeType){let o=r.textContent,i=r.nextSibling,a=0;for(;i&&i.nodeType===Node.TEXT_NODE;)o+=i.textContent,i=i.nextSibling,a++;const s=l(o,t);if(s){for(let e=0;e<a;e++)r.nextSibling.remove();n+=s.childNodes.length-1,e.replaceChild(s,r)}else n+=a}else if(1===r.nodeType){const e=" "+r.className+" ";-1===t.ignoredTags.indexOf(r.nodeName.toLowerCase())&&t.ignoredClasses.every((t=>-1===e.indexOf(" "+t+" ")))&&s(r,t)}}};var d=function(e,t){if(!e)throw new Error("No element provided to render");const n={};for(const e in t)t.hasOwnProperty(e)&&(n[e]=t[e]);n.delimiters=n.delimiters||[{left:"$$",right:"$$",display:!0},{left:"\\(",right:"\\)",display:!1},{left:"\\begin{equation}",right:"\\end{equation}",display:!0},{left:"\\begin{align}",right:"\\end{align}",display:!0},{left:"\\begin{alignat}",right:"\\end{alignat}",display:!0},{left:"\\begin{gather}",right:"\\end{gather}",display:!0},{left:"\\begin{CD}",right:"\\end{CD}",display:!0},{left:"\\[",right:"\\]",display:!0}],n.ignoredTags=n.ignoredTags||["script","noscript","style","textarea","pre","code","option"],n.ignoredClasses=n.ignoredClasses||[],n.errorCallback=n.errorCallback||console.error,n.macros=n.macros||{},s(e,n)}}(),o=o.default}()}));

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 680 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 944 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 853 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 839 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 768 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1,008 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 273 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 874 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 641 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 783 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 356 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 283 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 212 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 672 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 880 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 247 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 413 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 506 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 328 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 536 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 655 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 398 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 999 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 KiB

BIN
public/card.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

27
public/closable.js Normal file
View file

@ -0,0 +1,27 @@
const closable = document.querySelectorAll("details.closable");
closable.forEach((detail) => {
detail.addEventListener("toggle", () => {
if (detail.open) setTargetDetail(detail);
});
});
function setTargetDetail(targetDetail) {
closable.forEach((detail) => {
if (detail !== targetDetail) {
detail.open = false;
}
});
}
document.addEventListener("click", function (event) {
const isClickInsideDetail = [...closable].some((detail) =>
detail.contains(event.target)
);
if (!isClickInsideDetail) {
closable.forEach((detail) => {
detail.open = false;
});
}
});

406
public/comments.js Normal file
View file

@ -0,0 +1,406 @@
// Taken from https://carlschwan.eu/2020/12/29/adding-comments-to-your-static-blog-with-mastodon/
// Attachment, card, and spoiler code taken from https://github.com/cassidyjames/cassidyjames.github.io/blob/99782788a7e3ba3cc52d6803010873abd1b02b9e/_includes/comments.html#L251-L296
let blogPostAuthorText = document.getElementById("blog-post-author-text").textContent;
let boostsFromText = document.getElementById("boosts-from-text").textContent;
let dateLocale = document.getElementById("date-locale").textContent;
let favesFromText = document.getElementById("faves-from-text").textContent;
let host = document.getElementById("host").textContent;
let id = document.getElementById("id").textContent;
let lazyAsyncImage = document.getElementById("lazy-async-image").textContent;
let loadingText = document.getElementById("loading-text").textContent;
let noCommentsText = document.getElementById("no-comments-text").textContent;
let relAttributes = document.getElementById("rel-attributes").textContent;
let reloadText = document.getElementById("reload-text").textContent;
let sensitiveText = document.getElementById("sensitive-text").textContent;
let user = document.getElementById("user").textContent;
let viewCommentText = document.getElementById("view-comment-text").textContent;
let viewProfileText = document.getElementById("view-profile-text").textContent;
document.getElementById("load-comments").addEventListener("click", loadComments);
function escapeHtml(unsafe) {
return unsafe
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#039;");
}
function emojify(input, emojis) {
let output = input;
emojis.forEach((emoji) => {
let picture = document.createElement("picture");
let source = document.createElement("source");
source.setAttribute("srcset", escapeHtml(emoji.url));
source.setAttribute("media", "(prefers-reduced-motion: no-preference)");
let img = document.createElement("img");
img.className = "emoji";
img.setAttribute("src", escapeHtml(emoji.static_url));
img.setAttribute("alt", `:${emoji.shortcode}:`);
img.setAttribute("title", `:${emoji.shortcode}:`);
if (lazyAsyncImage == "true") {
img.setAttribute("decoding", "async");
img.setAttribute("loading", "lazy");
}
picture.appendChild(source);
picture.appendChild(img);
output = output.replace(`:${emoji.shortcode}:`, picture.outerHTML);
});
return output;
}
function loadComments() {
let commentsWrapper = document.getElementById("comments-wrapper");
commentsWrapper.innerHTML = "";
let loadCommentsButton = document.getElementById("load-comments");
loadCommentsButton.innerHTML = loadingText;
loadCommentsButton.disabled = true;
fetch(`https://${host}/api/v1/statuses/${id}/context`)
.then(function (response) {
return response.json();
})
.then(function (data) {
let descendants = data["descendants"];
if (
descendants &&
Array.isArray(descendants) &&
descendants.length > 0
) {
commentsWrapper.innerHTML = "";
descendants.forEach(function (status) {
console.log(descendants);
if (status.account.display_name.length > 0) {
status.account.display_name = escapeHtml(
status.account.display_name
);
status.account.display_name = emojify(
status.account.display_name,
status.account.emojis
);
} else {
status.account.display_name = status.account.username;
}
let instance = "";
if (status.account.acct.includes("@")) {
instance = status.account.acct.split("@")[1];
} else {
instance = host;
}
const isReply = status.in_reply_to_id !== id;
let op = false;
if (status.account.acct == user) {
op = true;
}
status.content = emojify(status.content, status.emojis);
let comment = document.createElement("article");
comment.id = `comment-${status.id}`;
comment.className = isReply ? "comment comment-reply" : "comment";
comment.setAttribute("itemprop", "comment");
comment.setAttribute("itemtype", "http://schema.org/Comment");
let avatarSource = document.createElement("source");
avatarSource.setAttribute(
"srcset",
escapeHtml(status.account.avatar)
);
avatarSource.setAttribute(
"media",
"(prefers-reduced-motion: no-preference)"
);
let avatarImg = document.createElement("img");
avatarImg.className = "avatar";
avatarImg.setAttribute(
"src",
escapeHtml(status.account.avatar_static)
);
avatarImg.setAttribute(
"alt",
`@${status.account.username}@${instance} avatar`
);
if (lazyAsyncImage == "true") {
avatarImg.setAttribute("decoding", "async");
avatarImg.setAttribute("loading", "lazy");
}
let avatarPicture = document.createElement("picture");
avatarPicture.appendChild(avatarSource);
avatarPicture.appendChild(avatarImg);
let avatar = document.createElement("a");
avatar.className = "avatar-link";
avatar.setAttribute("href", status.account.url);
avatar.setAttribute("rel", relAttributes);
avatar.setAttribute(
"title",
`${viewProfileText} @${status.account.username}@${instance}`
);
avatar.appendChild(avatarPicture);
comment.appendChild(avatar);
let instanceBadge = document.createElement("a");
instanceBadge.className = "instance";
instanceBadge.setAttribute("href", status.account.url);
instanceBadge.setAttribute(
"title",
`@${status.account.username}@${instance}`
);
instanceBadge.setAttribute("rel", relAttributes);
instanceBadge.textContent = instance;
let display = document.createElement("span");
display.className = "display";
display.setAttribute("itemprop", "author");
display.setAttribute("itemtype", "http://schema.org/Person");
display.innerHTML = status.account.display_name;
let header = document.createElement("header");
header.className = "author";
header.appendChild(display);
header.appendChild(instanceBadge);
comment.appendChild(header);
let permalink = document.createElement("a");
permalink.setAttribute("href", status.url);
permalink.setAttribute("itemprop", "url");
permalink.setAttribute("title", `${viewCommentText} ${instance}`);
permalink.setAttribute("rel", relAttributes);
permalink.textContent = new Date(
status.created_at
).toLocaleString(dateLocale, {
dateStyle: "long",
timeStyle: "short",
});
let timestamp = document.createElement("time");
timestamp.setAttribute("datetime", status.created_at);
timestamp.appendChild(permalink);
permalink.classList.add("external");
comment.appendChild(timestamp);
let main = document.createElement("main");
main.setAttribute("itemprop", "text");
if (status.sensitive == true || status.spoiler_text != "") {
let summary = document.createElement("summary");
if (status.spoiler_text == "") {
status.spoiler_text == sensitiveText;
}
summary.innerHTML = status.spoiler_text;
let spoiler = document.createElement("details");
spoiler.appendChild(summary);
spoiler.innerHTML += status.content;
main.appendChild(spoiler);
} else {
main.innerHTML = status.content;
}
comment.appendChild(main);
let attachments = status.media_attachments;
let SUPPORTED_MEDIA = ["image", "video", "gifv", "audio"];
let media = document.createElement("div");
media.className = "attachments";
if (
attachments &&
Array.isArray(attachments) &&
attachments.length > 0
) {
attachments.forEach((attachment) => {
if (SUPPORTED_MEDIA.includes(attachment.type)) {
let mediaElement;
switch (attachment.type) {
case "image":
mediaElement = document.createElement("img");
mediaElement.setAttribute("src", attachment.preview_url);
if (attachment.description != null) {
mediaElement.setAttribute("alt", attachment.description);
mediaElement.setAttribute("title", attachment.description);
}
if (lazyAsyncImage == "true") {
mediaElement.setAttribute("decoding", "async");
mediaElement.setAttribute("loading", "lazy");
}
if (status.sensitive == true) {
mediaElement.classList.add("spoiler");
}
media.appendChild(mediaElement);
break;
case "video":
mediaElement = document.createElement("video");
mediaElement.setAttribute("src", attachment.url);
mediaElement.setAttribute("controls", "");
if (attachment.description != null) {
mediaElement.setAttribute("aria-title", attachment.description);
mediaElement.setAttribute("title", attachment.description);
}
if (status.sensitive == true) {
mediaElement.classList.add("spoiler");
}
media.appendChild(mediaElement);
break;
case "gifv":
mediaElement = document.createElement("video");
mediaElement.setAttribute("src", attachment.url);
mediaElement.setAttribute("autoplay", "");
mediaElement.setAttribute("playsinline", "");
mediaElement.setAttribute("loop", "");
if (attachment.description != null) {
mediaElement.setAttribute("aria-title", attachment.description);
mediaElement.setAttribute("title", attachment.description);
}
if (status.sensitive == true) {
mediaElement.classList.add("spoiler");
}
media.appendChild(mediaElement);
break;
case "audio":
mediaElement = document.createElement("audio");
mediaElement.setAttribute("src", attachment.url);
mediaElement.setAttribute("controls", "");
if (attachment.description != null) {
mediaElement.setAttribute("aria-title", attachment.description);
mediaElement.setAttribute("title", attachment.description);
}
media.appendChild(mediaElement);
break;
}
let mediaLink = document.createElement("a");
mediaLink.setAttribute("href", attachment.url);
mediaLink.setAttribute("rel", relAttributes);
mediaLink.appendChild(mediaElement);
media.appendChild(mediaLink);
}
});
comment.appendChild(media);
}
let interactions = document.createElement("footer");
let boosts = document.createElement("a");
boosts.className = "boosts";
boosts.setAttribute("href", `${status.url}/reblogs`);
boosts.setAttribute("title", `${boostsFromText}`.replace("$INSTANCE", instance));
let boostsIcon = document.createElement("i");
boostsIcon.className = "icon";
boosts.appendChild(boostsIcon);
boosts.insertAdjacentHTML('beforeend', ` ${status.reblogs_count}`);
interactions.appendChild(boosts);
let faves = document.createElement("a");
faves.className = "faves";
faves.setAttribute("href", `${status.url}/favourites`);
faves.setAttribute("title", `${favesFromText}`.replace("$INSTANCE", instance));
let favesIcon = document.createElement("i");
favesIcon.className = "icon";
faves.appendChild(favesIcon);
faves.insertAdjacentHTML('beforeend', ` ${status.favourites_count}`);
interactions.appendChild(faves);
comment.appendChild(interactions);
if (status.card != null) {
let cardFigure = document.createElement("figure");
if (status.card.image != null) {
let cardImg = document.createElement("img");
cardImg.setAttribute("src", status.card.image);
cardImg.classList.add("no-hover");
cardFigure.appendChild(cardImg);
}
let cardCaption = document.createElement("figcaption");
let cardTitle = document.createElement("strong");
cardTitle.innerHTML = status.card.title;
cardCaption.appendChild(cardTitle);
if (status.card.description != null && status.card.description.length > 0) {
let cardDescription = document.createElement("p");
cardDescription.innerHTML = status.card.description;
cardCaption.appendChild(cardDescription);
}
cardFigure.appendChild(cardCaption);
let card = document.createElement("a");
card.className = "card";
card.setAttribute("href", status.card.url);
card.setAttribute("rel", relAttributes);
card.appendChild(cardFigure);
comment.appendChild(card);
}
if (op === true) {
comment.classList.add("op");
avatar.classList.add("op");
avatar.setAttribute(
"title",
`${blogPostAuthorText}: ` + avatar.getAttribute("title")
);
instanceBadge.classList.add("op");
instanceBadge.setAttribute(
"title",
`${blogPostAuthorText}: ` + instanceBadge.getAttribute("title")
);
}
commentsWrapper.innerHTML += comment.outerHTML;
});
}
else {
var statusText = document.createElement("p");
statusText.innerHTML = noCommentsText;
statusText.setAttribute("id", "comments-status");
commentsWrapper.appendChild(statusText);
}
loadCommentsButton.innerHTML = reloadText;
})
.catch(function (error) {
console.error('Error loading comments:', error);
})
.finally(function () {
loadCommentsButton.disabled = false;
});
}

57
public/copy-button.js Normal file
View file

@ -0,0 +1,57 @@
// Based on https://www.roboleary.net/2022/01/13/copy-code-to-clipboard-blog.html
document.addEventListener("DOMContentLoaded", function () {
let blocks = document.querySelectorAll("pre[class^='language-']");
blocks.forEach((block) => {
if (navigator.clipboard) {
// Code block header title
let title = document.createElement("span");
let lang = block.getAttribute("data-lang");
title.innerHTML = lang;
// Copy button icon
let icon = document.createElement("i");
icon.classList.add("icon");
// Copy button
let button = document.createElement("button");
let copyCodeText = document.getElementById("copy-code-text").textContent;
button.setAttribute("title", copyCodeText)
button.appendChild(icon);
// Code block header
let header = document.createElement("div");
header.classList.add("header");
header.appendChild(title);
header.appendChild(button);
// Container that holds header and the code block itself
let container = document.createElement("div");
container.classList.add("pre-container");
container.appendChild(header);
// Move code block into the container
block.parentNode.insertBefore(container, block);
container.appendChild(block);
button.addEventListener("click", async () => {
await copyCode(block, header, button); // Pass the button here
});
}
});
async function copyCode(block, header, button) {
let code = block.querySelector("code");
let text = code.innerText;
await navigator.clipboard.writeText(text);
header.classList.add("active");
button.setAttribute("disabled", true);
header.addEventListener("animationend", () => {
header.classList.remove("active");
button.removeAttribute("disabled");
}, { once: true });
}
});

271
public/count.js Normal file
View file

@ -0,0 +1,271 @@
// GoatCounter: https://www.goatcounter.com
// This file is released under the ISC license: https://opensource.org/licenses/ISC
;(function() {
'use strict';
if (window.goatcounter && window.goatcounter.vars) // Compatibility with very old version; do not use.
window.goatcounter = window.goatcounter.vars
else
window.goatcounter = window.goatcounter || {}
// Load settings from data-goatcounter-settings.
var s = document.querySelector('script[data-goatcounter]')
if (s && s.dataset.goatcounterSettings) {
try { var set = JSON.parse(s.dataset.goatcounterSettings) }
catch (err) { console.error('invalid JSON in data-goatcounter-settings: ' + err) }
for (var k in set)
if (['no_onload', 'no_events', 'allow_local', 'allow_frame', 'path', 'title', 'referrer', 'event'].indexOf(k) > -1)
window.goatcounter[k] = set[k]
}
var enc = encodeURIComponent
// Get all data we're going to send off to the counter endpoint.
var get_data = function(vars) {
var data = {
p: (vars.path === undefined ? goatcounter.path : vars.path),
r: (vars.referrer === undefined ? goatcounter.referrer : vars.referrer),
t: (vars.title === undefined ? goatcounter.title : vars.title),
e: !!(vars.event || goatcounter.event),
s: [window.screen.width, window.screen.height, (window.devicePixelRatio || 1)],
b: is_bot(),
q: location.search,
}
var rcb, pcb, tcb // Save callbacks to apply later.
if (typeof(data.r) === 'function') rcb = data.r
if (typeof(data.t) === 'function') tcb = data.t
if (typeof(data.p) === 'function') pcb = data.p
if (is_empty(data.r)) data.r = document.referrer
if (is_empty(data.t)) data.t = document.title
if (is_empty(data.p)) data.p = get_path()
if (rcb) data.r = rcb(data.r)
if (tcb) data.t = tcb(data.t)
if (pcb) data.p = pcb(data.p)
return data
}
// Check if a value is "empty" for the purpose of get_data().
var is_empty = function(v) { return v === null || v === undefined || typeof(v) === 'function' }
// See if this looks like a bot; there is some additional filtering on the
// backend, but these properties can't be fetched from there.
var is_bot = function() {
// Headless browsers are probably a bot.
var w = window, d = document
if (w.callPhantom || w._phantom || w.phantom)
return 150
if (w.__nightmare)
return 151
if (d.__selenium_unwrapped || d.__webdriver_evaluate || d.__driver_evaluate)
return 152
if (navigator.webdriver)
return 153
return 0
}
// Object to urlencoded string, starting with a ?.
var urlencode = function(obj) {
var p = []
for (var k in obj)
if (obj[k] !== '' && obj[k] !== null && obj[k] !== undefined && obj[k] !== false)
p.push(enc(k) + '=' + enc(obj[k]))
return '?' + p.join('&')
}
// Show a warning in the console.
var warn = function(msg) {
if (console && 'warn' in console)
console.warn('goatcounter: ' + msg)
}
// Get the endpoint to send requests to.
var get_endpoint = function() {
var s = document.querySelector('script[data-goatcounter]')
if (s && s.dataset.goatcounter)
return s.dataset.goatcounter
return (goatcounter.endpoint || window.counter) // counter is for compat; don't use.
}
// Get current path.
var get_path = function() {
var loc = location,
c = document.querySelector('link[rel="canonical"][href]')
if (c) { // May be relative or point to different domain.
var a = document.createElement('a')
a.href = c.href
if (a.hostname.replace(/^www\./, '') === location.hostname.replace(/^www\./, ''))
loc = a
}
return (loc.pathname + loc.search) || '/'
}
// Run function after DOM is loaded.
var on_load = function(f) {
if (document.body === null)
document.addEventListener('DOMContentLoaded', function() { f() }, false)
else
f()
}
// Filter some requests that we (probably) don't want to count.
goatcounter.filter = function() {
if ('visibilityState' in document && document.visibilityState === 'prerender')
return 'visibilityState'
if (!goatcounter.allow_frame && location !== parent.location)
return 'frame'
if (!goatcounter.allow_local && location.hostname.match(/(localhost$|^127\.|^10\.|^172\.(1[6-9]|2[0-9]|3[0-1])\.|^192\.168\.|^0\.0\.0\.0$)/))
return 'localhost'
if (!goatcounter.allow_local && location.protocol === 'file:')
return 'localfile'
if (localStorage && localStorage.getItem('skipgc') === 't')
return 'disabled with #toggle-goatcounter'
return false
}
// Get URL to send to GoatCounter.
window.goatcounter.url = function(vars) {
var data = get_data(vars || {})
if (data.p === null) // null from user callback.
return
data.rnd = Math.random().toString(36).substr(2, 5) // Browsers don't always listen to Cache-Control.
var endpoint = get_endpoint()
if (!endpoint)
return warn('no endpoint found')
return endpoint + urlencode(data)
}
// Count a hit.
window.goatcounter.count = function(vars) {
var f = goatcounter.filter()
if (f)
return warn('not counting because of: ' + f)
var url = goatcounter.url(vars)
if (!url)
return warn('not counting because path callback returned null')
if (!navigator.sendBeacon(url)) {
// This mostly fails due to being blocked by CSP; try again with an
// image-based fallback.
var img = document.createElement('img')
img.src = url
img.style.position = 'absolute' // Affect layout less.
img.style.bottom = '0px'
img.style.width = '1px'
img.style.height = '1px'
img.loading = 'eager'
img.setAttribute('alt', '')
img.setAttribute('aria-hidden', 'true')
var rm = function() { if (img && img.parentNode) img.parentNode.removeChild(img) }
img.addEventListener('load', rm, false)
document.body.appendChild(img)
}
}
// Get a query parameter.
window.goatcounter.get_query = function(name) {
var s = location.search.substr(1).split('&')
for (var i = 0; i < s.length; i++)
if (s[i].toLowerCase().indexOf(name.toLowerCase() + '=') === 0)
return s[i].substr(name.length + 1)
}
// Track click events.
window.goatcounter.bind_events = function() {
if (!document.querySelectorAll) // Just in case someone uses an ancient browser.
return
var send = function(elem) {
return function() {
goatcounter.count({
event: true,
path: (elem.dataset.goatcounterClick || elem.name || elem.id || ''),
title: (elem.dataset.goatcounterTitle || elem.title || (elem.innerHTML || '').substr(0, 200) || ''),
referrer: (elem.dataset.goatcounterReferrer || elem.dataset.goatcounterReferral || ''),
})
}
}
Array.prototype.slice.call(document.querySelectorAll("*[data-goatcounter-click]")).forEach(function(elem) {
if (elem.dataset.goatcounterBound)
return
var f = send(elem)
elem.addEventListener('click', f, false)
elem.addEventListener('auxclick', f, false) // Middle click.
elem.dataset.goatcounterBound = 'true'
})
}
// Add a "visitor counter" frame or image.
window.goatcounter.visit_count = function(opt) {
on_load(function() {
opt = opt || {}
opt.type = opt.type || 'html'
opt.append = opt.append || 'body'
opt.path = opt.path || get_path()
opt.attr = opt.attr || {width: '200', height: (opt.no_branding ? '60' : '80')}
opt.attr['src'] = get_endpoint() + 'er/' + enc(opt.path) + '.' + enc(opt.type) + '?'
if (opt.no_branding) opt.attr['src'] += '&no_branding=1'
if (opt.style) opt.attr['src'] += '&style=' + enc(opt.style)
if (opt.start) opt.attr['src'] += '&start=' + enc(opt.start)
if (opt.end) opt.attr['src'] += '&end=' + enc(opt.end)
var tag = {png: 'img', svg: 'img', html: 'iframe'}[opt.type]
if (!tag)
return warn('visit_count: unknown type: ' + opt.type)
if (opt.type === 'html') {
opt.attr['frameborder'] = '0'
opt.attr['scrolling'] = 'no'
}
var d = document.createElement(tag)
for (var k in opt.attr)
d.setAttribute(k, opt.attr[k])
var p = document.querySelector(opt.append)
if (!p)
return warn('visit_count: append not found: ' + opt.append)
p.appendChild(d)
})
}
// Make it easy to skip your own views.
if (location.hash === '#toggle-goatcounter') {
if (localStorage.getItem('skipgc') === 't') {
localStorage.removeItem('skipgc', 't')
alert('GoatCounter tracking is now ENABLED in this browser.')
}
else {
localStorage.setItem('skipgc', 't')
alert('GoatCounter tracking is now DISABLED in this browser until ' + location + ' is loaded again.')
}
}
if (!goatcounter.no_onload)
on_load(function() {
// 1. Page is visible, count request.
// 2. Page is not yet visible; wait until it switches to 'visible' and count.
// See #487
if (!('visibilityState' in document) || document.visibilityState === 'visible')
goatcounter.count()
else {
var f = function(e) {
if (document.visibilityState !== 'visible')
return
document.removeEventListener('visibilitychange', f)
goatcounter.count()
}
document.addEventListener('visibilitychange', f)
}
if (!goatcounter.no_events)
goatcounter.bind_events()
})
})();

48
public/css/gallery.css Normal file
View file

@ -0,0 +1,48 @@
#image-gallery {
margin: 2rem 0;
}
.gallery {
column-count: 3;
column-gap: 1rem;
}
.gallery-item {
break-inside: avoid;
margin-bottom: 1rem;
text-align: center;
list-style: none; /* ← important! */
}
.gallery-item img {
width: 100%;
height: auto;
border-radius: 12px;
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
transition: transform 0.2s ease;
}
.gallery-item img:hover {
transform: scale(1.6);
}
.caption {
font-size: 0.9rem;
margin-top: 0.5rem;
margin-bottom: 0; /* ← just in case */
color: var(--fg-color);
list-style: none; /* ← extra-safe */
}
@media (max-width: 768px) {
.gallery {
column-count: 2;
}
}
@media (max-width: 480px) {
.gallery {
column-count: 1;
}
}

28
public/css/mermaid.css Normal file
View file

@ -0,0 +1,28 @@
.mermaid {
text-align: center;
margin-top: 1.5em;
margin-bottom: 1.5em;
padding: 1em;
border-radius: 0.5em;
background-color: var(--code-bg);
font-family: var(--code-font, monospace);
font-size: 0.9rem;
overflow-x: auto;
max-width: 100%;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.08);
}
.mermaid strong {
font-weight: bold;
}
.mermaid svg {
max-width: 100%;
height: auto;
}
@media (prefers-color-scheme: dark) {
.mermaid {
background-color: var(--code-bg-dark, #2d2d2d);
}
}

72
public/css/skills.css Normal file
View file

@ -0,0 +1,72 @@
/* Basic Layout for Skills List */
#skills-content {
margin: 2rem 0;
}
#skills-content .category {
margin-bottom: 3rem;
}
#skills-content .category h3 {
font-size: 1.5rem;
font-weight: regular;
margin-bottom: 1rem;
}
/* Skills container for single-line display */
#skills-content .skills-list {
list-style: none;
padding: 0;
margin: 0;
display: flex; /* Use flexbox for single-line display */
flex-wrap: wrap; /* Allow skills to wrap if needed */
gap: 1rem; /* Spacing between skills */
}
/* Skill List Item */
#skills-content .skills-list .skill {
display: inline-flex; /* Ensures each skill is aligned inline */
align-items: center;
position: relative;
cursor: pointer; /* Show pointer cursor on hover */
text-decoration: none; /* Remove underline from links */
color: inherit; /* Inherit color from parent */
transition: background-color 0.3s, color 0.3s;
}
/* Skill Name */
#skills-content .skills-list .skill span {
display: inline-block;
font-size: 1.2rem;
margin-left: 0.5rem;
line-height: 1.4;
}
/* Skill Icon */
#skills-content .skills-list .skill .skill-icon {
margin-right: 0.5rem;
color: var(--accent-color);
font-size: 1.4rem;
vertical-align: middle;
}
/* Hover Effects for Skills */
#skills-content .skills-list .skill:hover::before {
background: var(--accent-color-dark);
}
#skills-content .skills-list .skill:hover {
background-color: var(--accent-color-alpha);
color: var(--fg-color); /* Change text color on hover */
}
/* Skill Category Hover Effect */
#skills-content .skills-list .skill:hover span {
color: var(--fg-color);
}
/* Optional: Customize Icon Color on Hover */
#skills-content .skills-list .skill:hover .skill-icon {
color: var(--fg-color);
}

91
public/css/timeline.css Normal file
View file

@ -0,0 +1,91 @@
/* Basic Layout */
#timeline-content {
position: relative;
margin: 2rem 0;
padding-left: 120px;
}
#timeline-content ul.timeline {
list-style: none;
padding: 0;
margin: 0;
position: relative;
}
#timeline-content ul.timeline::before {
content: '';
position: absolute;
left: -30px;
top: 0;
bottom: 0;
width: 2px;
background: var(--accent-color);
}
/* Event List Item */
#timeline-content li.event {
position: relative;
margin-bottom: 3rem;
}
/* Event Circle */
#timeline-content li.event::before {
content: '';
position: absolute;
left: -39px;
top: 5px;
width: 16px;
height: 16px;
border-radius: 50%;
background: var(--accent-color);
border: 2px solid white;
z-index: 1;
}
/* From + To Label — styled exactly like old date label */
#timeline-content li.event::after {
content: attr(data-from) "\A" attr(data-to);
white-space: pre; /* ensures newline works */
position: absolute;
left: -160px;
width: 100px;
text-align: right;
color: #ffffff;
font-weight: bold;
font-size: 0.9rem;
line-height: 1.3;
top: 0;
}
/* Event Heading */
#timeline-content li.event h3 {
margin: 0 0 0.5rem 0;
display: inline-block;
}
/* Event Description */
#timeline-content li.event p {
margin: 0;
}
/* Timeline Icon */
#timeline-content .timeline-icon {
margin-right: 0.5rem;
color: var(--accent-color);
font-size: 1.2rem;
vertical-align: middle;
}
/* Hover Effects */
#timeline-content li.event:hover::before {
background: var(--accent-color-dark);
}
#timeline-content li.event:hover {
background-color: var(--accent-color-alpha);
}
/* From + To Label Hover Effect */
#timeline-content li.event:hover::after {
color: var(--fg-color);
}

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