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_feed = true
show_theme_switcher = true show_theme_switcher = true
show_repo = true show_repo = true
links = [ links = [
{ name = "Menu", menu = [
{ url = "@/blog/_index.md", name = "Blog" }, { url = "@/blog/_index.md", name = "Blog" },
{ url = "@/pages/privacy.md", name = "Privacy" }, { url = "@/pages/contact.md", name = "Contact Me" },
{ url = "@/pages/cv.md", name = "Experience" }, { url = "@/pages/cv.md", name = "Experience" },
{ url = "@/pages/about.md", name = "About" }, { url = "@/pages/about.md", name = "About" },
] },
{ url = "https://kaesewerkstatt.petau.net", name = "Käsewerkstatt" }
] ]
[extra.footer] [extra.footer]
links = [ links = [
{ url = "@/blog/_index.md", name = "Blog" }, { 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 = [ 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://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://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://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_copyright = true
show_powered_by = false show_powered_by = false
# Whether to show link to website source # Whether to show link to website source
show_source = false show_source = false
#copyright = "© *Aperture* **Science** ~~Innovators~~, `1972`"
# Based on https://carlschwan.eu/2020/12/29/adding-comments-to-your-static-blog-with-mastodon/ # 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. # the needed rel="me" link is set in the head based on these.
[extra.comments] [extra.comments]
# Your Mastodon API host; instance that you have an account on. # 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. # Your Mastodon username; used to determine who the original poster is.
user = "daudix" user = "reprintedAron"
# Whether to show the QR code to Mastodon post # Whether to show the QR code to Mastodon post
show_qr = true show_qr = true

View file

@ -9,7 +9,7 @@ show_shares = true
+++ +++
# Welcome ## Welcome
to the online presence of Aron Petau. 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. 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. 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." "title": "A custom-built printer enclosure made up of 3 Ikea Lack tables and around 3 kgs of plastic."
} }
] ]
``` {% end %}
## 3D Printing ## 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" "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] [extra]
banner = "/images/beer_tap.jpg" banner = "beer_tap.jpg"
show_copyright = true show_copyright = true
show_shares = 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. 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: 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. 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" title = "Coding Examples"
date = 2022-03-01 date = 2021-03-01
authors = ["Aron Petau"] authors = ["Aron Petau"]
description = "A selection of coding projects from my Bachelor's in Cognitive Science" 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] [taxonomies]
tags = [ tags = [
"AI", "AI",
@ -47,7 +26,7 @@ tags = [
] ]
[extra] [extra]
banner = "/images/sample_lr.png" banner = "sample_lr.png"
show_copyright = true show_copyright = true
show_shares = 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. 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: 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. 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) ### 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. 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. reality of the human experience.
<embed <embed
src="/assets/documents/Human_Waste_MA_Aron_Petau.pdf" src="/documents/Human_Waste_MA_Aron_Petau.pdf"
type="application/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"> <div class="buttons centered">
<a class="colored external" href="https://pinry.petau.net">See the image archive yourself</a> <a class="big colored external"
href="https://pinry.petau.net">See the image archive yourself</a>
</div> </div>
<div class="buttons"> <div class="buttons centered">
<a class="colored external" href="https://archive.petau.net/#/graph">See the archive graph yourself</a> <a class="big colored external"
href="https://archive.petau.net/#/graph">See the archive graph yourself</a>
</div> </div>
<div class="buttons"> <div class="buttons centered">
<a class="colored external" href="https://forgejo.petau.net/aron/machine_archivist.git">Find the complete Repo on Forgejo</a> <a class="big colored external"
href="https://forgejo.petau.net/aron/machine_archivist.git">Find the complete Repo on Forgejo</a>
</div> </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. 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"> <div class="buttons">
<a class="colored external" href="https://t.me/apetau">Telegram</a> <a class="colored external" href="https://t.me/apetau">Telegram</a>
</div> </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>

View file

@ -2,6 +2,7 @@
title = "Curriculum vitae" title = "Curriculum vitae"
description = "Aron writes about their past experience" description = "Aron writes about their past experience"
authors = ["Aron Petau"] authors = ["Aron Petau"]
toc = true
+++ +++
## Who am I? ## Who am I?
@ -148,50 +149,189 @@ Contact me via [Email](mailto:aron@petau.net) for further questions.
] ]
{% end %} {% end %}
{% import "macros/skills.html" as skills %} ## Software Skills
<div>
{{ skills::display() | safe }}
</div>
{% 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 {% skills() %}
[
**HTML, CSS, JavaScript** \| GH-pages \| psychoJS \| AMPStack \| ibex-farm {
"name": "3D Printer",
**C#** \| Unity \| Unreal Engine \| Visual Studio "skills": [
{ "name": "Fused-Deposition Modeling (FDM)", "icon": "fas fa-cube", "link": "https://en.wikipedia.org/wiki/Fused_filament_fabrication" },
**CAD** \| Fusion 360 \| TinkerCAD \| Rhino/Grasshopper { "name": "Resin (MSLA)", "icon": "fas fa-cube", "link": "https://en.wikipedia.org/wiki/Stereolithography" }
]
**Slicing** \| PrusaSlicer \| kiri.moto \| Cura \| Lightburn },
{
**Images** \| Inkscape \| Illustrator \| GIMP \| Photoshop \| Lightroom \| Agisoft Metashape "name": "CNC",
"skills": [
**Video** \| Premiere \| DaVinci Resolve \| OBS \| TouchDesigner \| DJI Drones \| FPV Drones { "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/" }
**Audio** \| Audacity \| supercollider ]
},
**Office** \| MS-Office Suite \| Latex \| Markdown \| Typst {
"name": "Lasercutting",
**Educational** \| Scratch \| Makey-Makey \| Ozobot \| Cospaces "skills": [
{ "name": "Cricut", "icon": "fas fa-cut", "link": "https://cricut.com/" },
### Machine Skills { "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/" },
**3D Printer** \| Fused-Deposition Modeling (FDM)\| Resin (MSLA) { "name": "X-Tool S1", "icon": "fas fa-bolt", "link": "https://www.xtool.com/products/xtool-s1-laser-cutter" },
{ "name": "Scissors", "icon": "fas fa-scissors" }
**CNC** \| Nomad 3 \| Shaper Origin ]
},
**(Laser)cutter** \| Cricut \| Mr. Beam \| BRM Lasers Pro 1600 \| X-Tool S1 \| Scissors {
"name": "Textile",
**Textile** \| Brother Innov-is V3 Stitching Robot \| Sewing Machine \| Handstitching "skills": [
{ "name": "Brother Innov-is V3 Stitching Robot", "icon": "fas fa-tshirt", "link": "https://www.brothersewing.co.uk/products/innov-is-v3" },
**VR** \| Meta Quest 2 \| HTC Vive Pro { "name": "Sewing Machine", "icon": "fas fa-tshirt" },
{ "name": "Handstitching", "icon": "fas fa-hand-paper" }
**AR** \| Magic Leap 1 \| Apple AR Suite ]
},
**Microcontroller** \| Arduino \| Raspberry Pi \| ESP32 \| Pi Pico {
"name": "VR",
**Industrial robots** \| Universal Robots UR5 "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