Compare commits

..

2 commits

Author SHA1 Message Date
41ace4dad8 WIP: save local changes 2025-09-12 11:10:40 +02:00
0a6b3f0128 upd artist statement 2025-09-12 11:07:17 +02:00
68 changed files with 166 additions and 3457 deletions

View file

@ -40,8 +40,8 @@ styles = [
bundled_fonts = false bundled_fonts = false
issues_url = "https://forgejo.petau.net/aron/studio-umzu/issues" issues_url = "https://forgejo.petau.net/aron/studio-umzu/issues"
source_url = "https://forgejo.petau.net/aron/studio-umzu" source_url = "https://forgejo.petau.net/aron/studio-umzu"
default_theme = "dark" default_theme = "light"
accent_color = "#b12633" accent_color = "#ff0000"
accent_color_dark = "#c54854" accent_color_dark = "#c54854"
toc = true toc = true
toc_sidebar = true toc_sidebar = true
@ -61,22 +61,16 @@ show_feed = true
show_theme_switcher = true show_theme_switcher = true
show_repo = true show_repo = true
links = [ links = [
{ url = "https://friedrichwebergoizel.com", name = "About Friedrich" }, { url = "https://friedrichwebergoizel.com", name = "Friedrich", external = true },
{ url = "https://aron.petau.net", name = "About Aron" }, { url = "https://aron.petau.net", name = "Aron", external = true },
] ]
[extra.footer] [extra.footer]
links = [
{ url = "https://friedrichwebergoizel.com", name = "About Friedrich" },
{ url = "https://aron.petau.net", name = "About Aron" },
]
socials = [ socials = [
{ 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 = "mailto:aron@petau.net,f.goizel@yahoo.com?subject=Studio%20UM%3CZU", name = "Email", icon = "%3Csvg role='img' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Ctitle%3EEmail%3C/title%3E%3Cpath d='M1.5 4.5h21a1.5 1.5 0 0 1 1.5 1.5v12a1.5 1.5 0 0 1-1.5 1.5h-21A1.5 1.5 0 0 1 0 18V6a1.5 1.5 0 0 1 1.5-1.5zm10.5 7.125L2.25 6.75v.375L12 12.75l9.75-5.625V6.75l-9.75 4.875z'/%3E%3C/svg%3E" }
{ url = "mailto:aron@petau.net,f.goizel@yahoo.com?subject=[Workshop]", name = "Mail", icon = "%3Csvg role='img' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Ctitle%3EEmail%3C/title%3E%3Cpath d='M1.5 4.5h21a1.5 1.5 0 0 1 1.5 1.5v12a1.5 1.5 0 0 1-1.5 1.5h-21A1.5 1.5 0 0 1 0 18V6a1.5 1.5 0 0 1 1.5-1.5zm0 1.5v.243l10.5 7.006 10.5-7.006V6zm21 12V8.132l-10.038 6.697a.75.75 0 0 1-.924 0L1.5 8.132V18z'/%3E%3C/svg%3E" },
] ]
show_copyright = true show_copyright = true
show_powered_by = false show_powered_by = false

View file

@ -1,49 +1,99 @@
+++ +++
title = "Studio UMZU" title = "Werkraum - Making Workshops"
description = "Die Zukunft gestalten — mit Händen, Köpfen und Code. Buchen Sie uns für Ihren nächsten digitalen / handwerklichen Workshop" description = "Die Zukunft gestalten — mit Händen, Köpfen und Code. Buchen Sie uns für Ihren nächsten Digital- oder Handwerksworkshop."
draft=false
[extra] [extra]
show_copyright = false
show_shares = true show_shares = true
show_source = false show_source = false
show_toc = true show_toc = true
inline_toc = true inline_toc = true
[extra.footer]
copyright = "© Test"
show_copyright = false
+++ +++
{% alert(note=true) %}
Dies ist ein Work in Progress.
Die hier bereitgestellten Informationen können sich noch ändern.
{% end %}
## Euer Kreativ-Workshop-Team ## Euer Kreativ-Workshop-Team
<aside> Wir sind Aron Petau und Friedrich Weber Goizel — Maker, Pädagogen und leidenschaftliche Tüftler.
Wir entwickeln und veranstalten Workshops in Berlin.
![Logo](/logo.png "Studio UMZU") Wir bringen die Welt des Machens in Bibliotheken, Schulen und Jugendzentren.
Unsere Workshops sind praxisnah und darauf ausgelegt, Kreativität, Neugier und technische Fähigkeiten zu fördern.
</aside> Egal, ob ihr schon einen Makerspace mit 3D-Druckern, Lasercuttern oder Plottern habt oder ganz am Anfang steht — wir passen uns an.
Wir bieten sowohl mobile Vorführungen und Einführungsveranstaltungen als auch vertiefende Workshops, die auf eurer bestehenden Ausstattung aufbauen.
Jeder Workshop wird individuell auf eure Bedürfnisse, euer Publikum und euren Raum zugeschnitten.
Wir sind Studio UMZU Aron Petau und Friedrich Weber Goizel — Maker, Pädagogen und leidenschaftliche Tüftler, die in Berlin Workshops konzipieren und umsetzen. - Workshops zu 3D-Druck, Lasercutting, Plotten, Robotik und 3D-Design
- Flexible Durchführung: auf eurem Equipment oder mit unseren mobilen Demo-Setups
- Erfahrung mit Schulklassen, Bibliotheken und öffentlichem Publikum
- Maßgeschneiderte Workshop-Konzepte passend zu euren Zielen und Teilnehmenden
- Wir helfen euch sogar bei der Planung, Gestaltung und dem Aufbau neuer Makerspaces!
Wir bringen die Welt des Machens in Bibliotheken, Schulen und Jugendzentren und bieten praktische Workshops, die Kreativität, Neugier und technisches Können fördern. Lasst uns gemeinsam einen Raum schaffen, in dem Handwerk auf Code, Analoges auf Digitales und Ideen auf Leben treffen.
Ihr könnt uns für einmalige Veranstaltungen oder laufende Programme kontaktieren.
Ob Sie bereits einen Makerspace mit 3D-Druckern, Lasercuttern oder Plottern haben oder gerade erst anfangen wir passen uns an.
Wir bieten sowohl mobile Vorführungen und Einführungen als auch vertiefende Workshops, die auf Ihrer vorhandenen Ausstattung aufbauen.
Jeder Workshop wird individuell auf Ihre Bedürfnisse, Zielgruppen und Räume zugeschnitten.
- Workshops zu 3D-Druck, Lasercutting, Plotten, Robotik und 3D-Design
- Flexible Durchführung: auf Ihrer Ausstattung oder mit unseren mobilen Demo-Setups
- Erfahrung mit Schulklassen, Bibliotheken und öffentlichem Publikum
- Individuelle Workshop-Konzepte passend zu Ihren Zielen und Teilnehmenden
- Wir helfen sogar beim Entwerfen, Kuratieren und Aufbau neuer Makerspaces!
Lasst uns gemeinsam Räume schaffen, in denen Handwerk auf Code trifft, Analoges auf Digitales und Ideen lebendig werden.
Wir helfen gerne dabei, euren Ort in einen Knotenpunkt für Kreativität und Lernen zu verwandeln.
Kontaktiert uns für einmalige Veranstaltungen oder langfristige Programme.
--- ---
## Kontakt ## Kontakt
Wir freuen uns, von euch zu hören! Wir freuen uns, von euch zu hören!
Für Anfragen, Buchungen oder zur Besprechung individueller Workshop-Konzepte erreicht ihr uns per E-Mail: Für Anfragen, Buchungen oder um über einen individuellen Workshop zu sprechen, schreibt uns:
[aron@petau.net + f.goizel@yahoo.com](mailto:aron@petau.net,f.goizel@yahoo.com?subject=[Workshop]) [aron@petau.net + f.goizel@yahoo.com]("mailto:aron@petau.net,f.goizel@yahoo.com?subject=Studio%20UM%3CZU")
---
## Unsere Profile
### Aron
<aside>
![Aron](/images/colleagues/aron_petau.jpeg "Aron Petau")
</aside>
Aron hat einen Hintergrund in Kognitionswissenschaften, KI und Mediendidaktik.
Er liebt knifflige Software-Probleme und denkt gerne über Plastik als Werkstoff jenseits des Druckers nach.
Er hat bereits im Grundstudium im Rahmen des Forschungsprojektes [UOS.DLL Digitales Lernen Leben](https://lehrportal.uni-osnabrueck.de/uosdll/) Makerspaces mit konzipiert und aufgebaut.
Auch im künstlerischen Master, [Design and Computation](https://www.newpractice.net/study) beschäftigte er sich viel mit Technikdidaktik und technischen Zukünften.
Besonders im Forschungsprogramm [InKüLe](https://inkuele.de), Innovationen für die künstlerische Lehre an der UdK, konnte er viel Erfahrung mit neuen Lehr- und Lernformen sammeln, von Eventbetreuung durch eigene Livestreaminglösungen hin zur Konzeption und Durchführung eigener Workshops zu KI und Virtuellen Realitäten.
In der Zusammenarbeit im [studio einszwovier](https://www.gvb-berlin.de/unterricht-plus/arbeitsgemeinschaften/maker-space-studio-einszwovier/) mit Friedrich konnte Aron auch seine Masterarbeit zu Designpraxis als peer-learning Format im schulischen Unterricht erproben.
Hier gibts eine ausführliche [Bio](/pages/cv):
### Friedrich
<aside>
![Friedrich](/images/colleagues/friedrich_weber.jpg "Friedrich Weber Goizel")
</aside>
Friedrich Weber Goizel studierte zunächst Freie Kunst an der [UdK Berlin](https://www.udk-berlin.de/).
Später kamen ein Studium der [Humanoiden Robotik](https://www.bht-berlin.de/b-hrob) (BHT) und [Design & Computation](https://www.newpractice.net/study) dazu.
2022 war er Stipendiat der Kulturstiftung im Programm *„dive in. Programm für digitale Interaktionen“*, in dem der Einsatz von Robotik in öffentlichen Bibliotheken untersucht wurde.
Seitdem gibt er selbständig Workshops im Bereich „Making“ an verschiedenen Bibliotheken Berlins.
Als Workshopleiter ist er ebenfalls für die [Jungen Tüftler*innen, Berlin](https://junge-tueftler.de) an diversen Orten wie Stadtteilbibliotheken, Makerspaces oder dem Futurium tätig.
Neben der Leitung des Makerspaces [studio einszwovier](https://www.gvb-berlin.de/unterricht-plus/arbeitsgemeinschaften/maker-space-studio-einszwovier/) an einem Berliner Gymnasium, gemeinsam mit Aron, arbeitet Friedrich zudem als Konzept- und Medienkünstler.
Besonders wichtig ist es ihm, Begeisterung für Technik zu wecken, einen niederschwelligen Einstieg zu geben, gemeinsam Neues auszuprobieren und eine Lernumgebung zu gestalten, in der auf spielerische Weise ein kreativer Zugang zu Technik entsteht.
Finde [hier](https://friedrichwebergoizel.com) mehr über Friedrich heraus.
--- ---
@ -103,59 +153,3 @@ Für Anfragen, Buchungen oder zur Besprechung individueller Workshop-Konzepte er
} }
] ]
{% end %} {% end %}
---
## Unsere Profile
Wir sind Co-Leads in einem von Berlins neuesten und charmantesten Makerspaces, dem [studio einszwovier](https://www.gvb-berlin.de/unterricht-plus/arbeitsgemeinschaften/maker-space-studio-einszwovier/).
Kommt uns dort besuchen!
### Aron
<aside>
![Aron](/images/colleagues/aron_petau.jpeg "Aron Petau")
</aside>
Aron hat einen Hintergrund in Kognitionswissenschaften, KI und Mediendidaktik.
Er liebt knifflige Softwareprobleme und betrachtet Kunststoffe gerne als Material für das Machen über den 3D-Druck hinaus.
Neben Workshops mit den [Jungen Tüftler*innen, Berlin](https://junge-tueftler.de) hat Aron auch mehrjährige Erfahrung in der Konzeption und Ausstattung von Maker-Umgebungen, z. B. mit
[InKüLe](https://inkuele.de), Innovationen für die künstlerische Lehre an der UdK, aber auch mit dem [DigiLab](https://digitale-lehre.uni-osnabrueck.de/uos-digilab/) an der Universität Osnabrück.
Mehr Infos gibts in seiner [Bio](https://aron.petau.net/pages/cv).
### Friedrich
<aside>
![Friedrich](/images/colleagues/friedrich_weber.jpg "Friedrich Weber Goizel")
</aside>
#### Ausbildung:
[Junge Tüftler*innen, Berlin](https://junge-tueftler.de)
20142015 Studium der Bildenden Kunst, Hochschule der Künste Braunschweig, Bildhauerei
20152019 Studium der Bildenden Kunst, Universität der Künste Berlin, Konzeptkunst und Neue Medien
2019 Master of Fine Arts
20182021 Studium der humanoiden Robotik, Berliner Hochschule für Technik
2022 Studium Design & Computation, Technische Universität Berlin & Universität der Künste Berlin
#### Beruf:
20192021 Besucherservice im Futurium Berlin
20212022 Besucherservice und Ausstellungsaufsicht bei GID Berlin [Futurium, Berlin](https://futurium.de)
2022 Künstlerisches Forschungsstipendium "KuRoBi4all" (Kunst, Robotik, Bibliothek für alle); Hochschule für Wirtschaft und Recht Berlin, Stadtbibliotheken Pankow Berlin, Artspring Berlin
Mehr über Friedrich findet ihr [hier](https://friedrichwebergoizel.com).

View file

@ -1,24 +1,29 @@
+++ +++
title = "Studio UMZU" title = "Werkraum - Making Workshops"
description = "Crafting the future — hands, minds, and code. Book us for your next digital / crafts workshop" description = "Crafting the future — hands, minds, and code. Book us for your next digital / crafts workshop"
draft=false
[extra] [extra]
show_copyright = false
show_shares = true show_shares = true
show_source = false show_source = false
show_toc = true show_toc = true
inline_toc = true inline_toc = true
[extra.footer]
copyright = "© Test"
show_copyright = false
+++ +++
{% alert(note=true) %}
This is a work in Progress.
Informations on here are subject to change.
{% end %}
## Your Creative Workshop Team ## Your Creative Workshop Team
<aside> We are Aron Petau and Friedrich Weber Goizel — makers, educators, and passionate tinkerers conceptualizing and making workshops for a living in Berlin.
![Logo](/logo.png "Studio UMZU")
</aside>
We are Studio UMZU - Aron Petau and Friedrich Weber Goizel — makers, educators, and passionate tinkerers conceptualizing and making workshops for a living in Berlin.
We bring the world of making to libraries, schools, and youth centers, offering hands-on workshops designed to spark creativity, curiosity, and technical skill. We bring the world of making to libraries, schools, and youth centers, offering hands-on workshops designed to spark creativity, curiosity, and technical skill.
@ -26,11 +31,11 @@ Whether you already have a makerspace with 3D printers, laser cutters, or plotte
We can offer both portable showcases and introductory sessions as well as deep-dive workshops that build on your existing equipment. We can offer both portable showcases and introductory sessions as well as deep-dive workshops that build on your existing equipment.
We tailor every workshop to your needs, audience, and space. We tailor every workshop to your needs, audience, and space.
- Workshops in 3D printing, laser cutting, plotting, robotics, and 3D design - Workshops in 3D printing, laser cutting, plotting, robotics, and 3D design
- Flexible delivery: on your equipment or with our portable demo setups - Flexible delivery: on your equipment or with our portable demo setups
- Experience with school groups, libraries, and public audiences - Experience with school groups, libraries, and public audiences
- Custom workshop concepts to match your goals and participants - Custom workshop concepts to match your goals and participants
- We even help design, curate, and build out new maker spaces! - We even help design, curate, and build out new maker spaces!
Lets co-create a space where craft meets code, analog meets digital, and ideas come alive. Lets co-create a space where craft meets code, analog meets digital, and ideas come alive.
Wed love to help turn your place into a hub of creativity and learning. Wed love to help turn your place into a hub of creativity and learning.
@ -43,10 +48,61 @@ You can contact us for one-time events or ongoing programs.
Wed love to hear from you! Wed love to hear from you!
For inquiries, bookings, or to discuss a custom workshop, reach out to us via email at: For inquiries, bookings, or to discuss a custom workshop, reach out to us via email at:
[aron@petau.net + f.goizel@yahoo.com](mailto:aron@petau.net,f.goizel@yahoo.com?subject=[Workshop]) [aron@petau.net + f.goizel@yahoo.com]("mailto:aron@petau.net,f.goizel@yahoo.com?subject=Studio%20UM%3CZU")
--- ---
## Our Profiles
### Aron
<aside>
![Aron](/images/colleagues/aron_petau.jpeg "Aron Petau")
</aside>
Aron has a background in Cognitive Science, AI, and Media Didactics.
He enjoys tackling tricky software problems and thinking about plastic as a material beyond the 3D printer.
Already during his undergraduate studies, as part of the research project [UOS.DLL Digital Learning Living](https://lehrportal.uni-osnabrueck.de/uosdll/), he helped conceptualize and set up makerspaces.
In his artistic Masters program, [Design and Computation](https://www.newpractice.net/study), he also focused heavily on technology didactics and technological futures.
Within the research program [InKüLe](https://inkuele.de) (*Innovations for Artistic Teaching* at UdK), he gained extensive experience with new forms of teaching and learning — from event support through his own livestreaming solutions to designing and running workshops on AI and Virtual Realities.
Through his collaboration with Friedrich at [studio einszwovier](https://www.gvb-berlin.de/unterricht-plus/arbeitsgemeinschaften/maker-space-studio-einszwovier/), Aron was also able to test his Masters thesis on design practice as a peer-learning format in school education.
Read up a full [bio](https://aron.petau.net/pages/cv) here:
### Friedrich
<aside>
![Friedrich](/images/colleagues/friedrich_weber.jpg "Friedrich Weber Goizel")
</aside>
Friedrich Weber Goizel first studied Fine Arts at the [Berlin University of the Arts (UdK)](https://www.udk-berlin.de/).
He later pursued studies in [Humanoid Robotics](https://www.bht-berlin.de/b-hrob) (BHT) as well as in [Design and Computation](https://www.newpractice.net/study).
In 2022, he was a fellow of the Cultural Foundations program *“dive in. Program for Digital Interactions”*, where he explored the use of robotics in public libraries.
Since then, he has been independently leading “Making” workshops in various libraries across Berlin.
As a workshop facilitator, he also works with the [Junge Tüftler*innen, Berlin](https://junge-tueftler.de) initiative, running programs at neighborhood libraries, makerspaces, and institutions such as the Futurium.
Alongside Aron, he co-leads the makerspace [studio einszwovier](https://www.gvb-berlin.de/unterricht-plus/arbeitsgemeinschaften/maker-space-studio-einszwovier/) at a Berlin secondary school and also works as a conceptual and media artist.
What matters most to him is sparking enthusiasm for technology, providing easy points of entry, experimenting together, and creating a learning environment where playful exploration leads to a creative engagement with technology.
Engage more with Friedrich [here](https://friedrichwebergoizel.com).
---
## Our combined workshop skills ## Our combined workshop skills
{% skills() %} {% skills() %}
@ -104,56 +160,3 @@ For inquiries, bookings, or to discuss a custom workshop, reach out to us via em
] ]
{% end %} {% end %}
---
## Our Profiles
We are Co-Leads in one of Berlins newest and cutest Makerspaces, the [studio einszwovier](https://www.gvb-berlin.de/unterricht-plus/arbeitsgemeinschaften/maker-space-studio-einszwovier/).
Come visit us there!
### Aron
<aside>
![Aron](/images/colleagues/aron_petau.jpeg "Aron Petau")
</aside>
Aron has a background in Cognitive sciences, AI and Media Didactics.
He loves tricky software problems and likes to think of plastics as a material for making beyond the printer.
Besides making Workshops with the [Junge Tüftler*innen, Berlin](https://junge-tueftler.de), Aron also has a few years of experience conceptualizing and outfitting making environments, for example with
[InKüLe](https://inkuele.de), Innovationen für die künstlerische Lehre an der UdK, but also the [DigiLab](https://digitale-lehre.uni-osnabrueck.de/uos-digilab/) at the University of Osnabrück.
Read up a full [bio](https://aron.petau.net/pages/cv) here:
### Friedrich
<aside>
![Friedrich](/images/colleagues/friedrich_weber.jpg "Friedrich Weber Goizel")
</aside>
#### Education:
[Junge Tüftler*innen, Berlin](https://junge-tueftler.de)
2014-2015 Studies of Fine Arts, Hochschule der Künste Braunschweig, sculpture
2015-2019 Studies of Fine Arts, Universität der Künste Berlin, Concept and New Media
2019 Master of Fine Arts
2018-2021 Studies of humanoid Robotics, Berliner Hochschule für Technik
2022 Studies of Design & Computation, Technische Universität Berlin & Universität der Künste Berlin
#### Work:
2019-2021 Visitor service at Futurium Berlin
2021-2022 Visitor service and exhibition supervisor at GID Berlin [Futurium, Berlin](https://futurium.de)
2022 Artistic Research Scholarship "KuRoBi4all" (Art, Robotics, Library for all); Hochschule für Wirtschaft und Recht Berlin, Stadtbibliotheken Pankow Berlin, Artspring Berlin
Read up more on Friedrich [here](https://friedrichwebergoizel.com).

16
old_config.toml Normal file
View file

@ -0,0 +1,16 @@
# The URL the site will be built for
base_url = "https://studio-umzu.de"
# Whether to automatically compile all Sass files in the sass directory
compile_sass = true
# Whether to build a search index to be used later on by a JavaScript library
build_search_index = true
[markdown]
# Whether to do syntax highlighting
# Theme can be customised by setting the `highlight_theme` variable to a theme supported by Zola
highlight_code = true
[extra]
# Put all your custom variables here

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4 KiB

View file

@ -1,2 +0,0 @@
<!doctype html><html data-theme=dark lang=en xmlns=http://www.w3.org/1999/xhtml><head><meta charset=UTF-8><meta name=description><meta content="width=device-width,initial-scale=1" name=viewport><meta content=#b12633 name=theme-color><meta content=#c54854 media=(prefers-color-scheme:dark) name=theme-color><title>404 - Studio UMZU</title><link href=/ rel=canonical><link href=https://mastodon.online/@reprintedAron rel=me><meta content=@reprintedAron@mastodon.online name=fediverse:creator><link href=https://studio-umzu.de/favicon.png rel=icon type=image/png><link href=https://studio-umzu.de/apple-touch-icon.png rel=apple-touch-icon sizes=180x180 type=image/png><link crossorigin href=https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/css/all.min.css integrity=sha512-... referrerpolicy=no-referrer rel=stylesheet><link title="Studio UMZU - RSS Feed" href=https://studio-umzu.de/rss.xml rel=alternate type=application/rss+xml><link title="Studio UMZU - Atom Feed" href=https://studio-umzu.de/atom.xml rel=alternate type=application/atom+xml><style>:root{--accent-color:#b12633}[data-theme=dark]{--accent-color:#c54854}@media (prefers-color-scheme:dark){:root:not([data-theme=light]){--accent-color:#c54854}}</style><link href=https://studio-umzu.de/style.css rel=stylesheet><link href=https://studio-umzu.de/css/timeline.css rel=stylesheet><link href=https://studio-umzu.de/css/mermaid.css rel=stylesheet><link href=https://studio-umzu.de/css/skills.css rel=stylesheet><link href=https://studio-umzu.de/css/gallery.css rel=stylesheet><script defer src=https://studio-umzu.de/closable.js></script><script defer src=https://studio-umzu.de/copy-button.js></script><script data-goatcounter=https://awebsite.goatcounter.com/count defer src=https://studio-umzu.de/count.js></script><script defer src=https://studio-umzu.de/fuse.js></script><script defer src=https://studio-umzu.de/search-fuse.js></script><script defer src=https://studio-umzu.de/theme-switcher.js></script><script type=module>import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs';
mermaid.initialize({ startOnLoad: true });</script><meta content="Studio UMZU" property=og:site_name><meta content="404 - Studio UMZU" property=og:title><meta content=/ property=og:url><meta property=og:description><meta content=https://studio-umzu.de/card.png property=og:image><meta content=en_US property=og:locale><body><header id=site-nav><nav><a href=#main-content tabindex=0> Skip to Main Content </a><ul><li id=home><a href=https://studio-umzu.de> <i class=icon></i>Studio UMZU</a><li class=divider><li><a class=external href=https://friedrichwebergoizel.com>About Friedrich</a><li><a class=external href=https://aron.petau.net>About Aron</a><li id=search><button class=circle id=search-toggle title=Search><i class=icon></i></button><li id=language-switcher><details class=closable><summary class=circle title=Language><i class=icon></i></summary> <ul><li><a href=https://studio-umzu.de//de/ lang=de>Deutsch</a></ul></details><li id=theme-switcher><details class=closable><summary class=circle title=Theme><i class=icon></i></summary> <ul><li><button title="Switch to Light Theme" class=circle id=theme-light><i class=icon></i></button><li><button title="Switch to Dark Theme" class=circle id=theme-dark><i class=icon></i></button><li><button title="Use System Theme" class=circle id=theme-system><i class=icon></i></button></ul></details><li id=feed><details class=closable><summary class=circle title=Feed><i class=icon></i></summary> <ul><li><a href=https://studio-umzu.de/rss.xml>RSS</a><li><a href=https://studio-umzu.de/atom.xml>Atom</a></ul></details><li id=repo><a class=circle href=https://forgejo.petau.net/aron/studio-umzu title=Repository> <i class=icon></i> </a></ul></nav><div id=search-container><label class=visually-hidden for=search-bar>Search</label><input placeholder="Search for…" autocomplete=off disabled id=search-bar type=search><div id=search-results-container><div id=search-results></div></div></div></header><main id=main-content><picture><source media="(prefers-reduced-motion: reduce)" srcset=https://studio-umzu.de/404.png><img class="pixels transparent no-hover" alt=404 id=not-found src=https://studio-umzu.de/404.gif></picture><h1>Page Not Found</h1><p>The requested page could not be found. If you feel this is not normal, then you can create an issue on the issue tracker.<div class=buttons><a href=https://studio-umzu.de>Go Home</a><a class="colored external" href=https://forgejo.petau.net/aron/studio-umzu/issues>File an Issue</a></div><span class=hidden id=copy-code-text>Copy Code</span><span class=hidden id=search-index>https://studio-umzu.de/search_index.en.json</span><span class=hidden id=more-matches-text>$MATCHES more matches</span></main><footer id=site-footer><div class=carbonbadge id=wcb></div><script defer src=https://unpkg.com/website-carbon-badges@1.1.3/b.min.js></script><p><nav><ul><li><a class=external href=https://friedrichwebergoizel.com>About Friedrich</a><li><a class=external href=https://aron.petau.net>About Aron</a></ul></nav><p>© Studio UMZU, 2025<p><a class=external href=https://forgejo.petau.net/aron/studio-umzu>Website source</a><ul id=socials><li><a rel=" me" href=https://github.com/arontaupe title=GitHub> <i style="--icon:url(&#34;data:image/svg+xml,%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&#34;)" class=icon></i> <span>GitHub</span> </a><li><a href="mailto:aron@petau.net,f.goizel@yahoo.com?subject=[Workshop]" rel=" me" title=Mail> <i style="--icon:url(&#34;data:image/svg+xml,%3Csvg role='img' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Ctitle%3EEmail%3C/title%3E%3Cpath d='M1.5 4.5h21a1.5 1.5 0 0 1 1.5 1.5v12a1.5 1.5 0 0 1-1.5 1.5h-21A1.5 1.5 0 0 1 0 18V6a1.5 1.5 0 0 1 1.5-1.5zm0 1.5v.243l10.5 7.006 10.5-7.006V6zm21 12V8.132l-10.038 6.697a.75.75 0 0 1-.924 0L1.5 8.132V18z'/%3E%3C/svg%3E&#34;)" class=icon></i> <span>Mail</span> </a></ul></footer>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 340 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

View file

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
<title>Studio UMZU</title>
<link rel="self" type="application/atom+xml" href="https://studio-umzu.de/atom.xml"/>
<link rel="alternate" type="text/html" href="https://studio-umzu.de/"/>
<generator uri="https://www.getzola.org/">Zola</generator>
<id>https://studio-umzu.de/atom.xml</id>
</feed>

View file

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

Before

Width:  |  Height:  |  Size: 1.2 MiB

View file

@ -1,27 +0,0 @@
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;
});
}
});

View file

@ -1,406 +0,0 @@
// 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;
});
}

View file

@ -1,57 +0,0 @@
// 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 });
}
});

View file

@ -1,271 +0,0 @@
// 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()
})
})();

View file

@ -1,48 +0,0 @@
#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;
}
}

View file

@ -1,28 +0,0 @@
.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);
}
}

View file

@ -1,72 +0,0 @@
/* 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);
}

View file

@ -1,91 +0,0 @@
/* 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(--bg-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: var(--fg-color);
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);
}
#timeline-content li.event:hover {
background-color: var(--accent-color-alpha);
}
/* From + To Label Hover Effect */
#timeline-content li.event:hover::after {
color: var(--accent-color);
}

View file

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="de">
<title>Studio UMZU</title>
<subtitle>Spielerisches Lernen mit Technikdidaktik</subtitle>
<link rel="self" type="application/atom+xml" href="https://studio-umzu.de/de/atom.xml"/>
<link rel="alternate" type="text/html" href="https://studio-umzu.de/"/>
<generator uri="https://www.getzola.org/">Zola</generator>
<id>https://studio-umzu.de/de/atom.xml</id>
</feed>

File diff suppressed because one or more lines are too long

Binary file not shown.

Before

Width:  |  Height:  |  Size: 548 B

View file

@ -1 +0,0 @@
@font-face{font-style:normal;font-weight:100 900;src:url("fonts/InterVariable.woff2") format("woff2");font-family:"Inter Variable";font-display:swap}@font-face{font-style:italic;font-weight:100 900;src:url("fonts/InterVariable-Italic.woff2") format("woff2");font-family:"Inter Variable";font-display:swap}@font-face{font-style:normal;font-weight:100 900;src:url("fonts/JetBrainsMono.woff2") format("woff2");font-family:"JetBrains Mono";font-display:swap}@font-face{font-style:italic;font-weight:100 900;src:url("fonts/JetBrainsMono-Italic.woff2") format("woff2");font-family:"JetBrains Mono";font-display:swap}body{font-family:"Inter Variable",var(--font-system-ui),var(--font-emoji)}h1,h2,h3,h4,h5,h6{font-weight:bold;font-family:"Inter Variable",var(--font-system-ui),var(--font-emoji)}h1{font-weight:900}pre,code,kbd,samp{font-family:"JetBrains Mono",var(--font-monospace-code)}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

Before

Width:  |  Height:  |  Size: 183 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

File diff suppressed because one or more lines are too long

View file

@ -1,8 +0,0 @@
document.addEventListener("DOMContentLoaded", function () {
renderMathInElement(document.body, {
delimiters: [
{ left: "$$", right: "$$", display: true },
{ left: "$", right: "$", display: false },
],
});
});

File diff suppressed because it is too large Load diff

1
public/katex.min.js vendored

File diff suppressed because one or more lines are too long

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 103 KiB

View file

@ -1,4 +0,0 @@
User-agent: *
Disallow:
Allow: /
Sitemap: https://studio-umzu.de/sitemap.xml

View file

@ -1,12 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
<channel>
<title>Studio UMZU</title>
<link>https://studio-umzu.de/</link>
<description></description>
<generator>Zola</generator>
<language>en</language>
<atom:link href="https://studio-umzu.de/rss.xml" rel="self" type="application/rss+xml"/>
</channel>
</rss>

View file

@ -1,209 +0,0 @@
// Based on https://github.com/getzola/zola/blob/1ac1231de1e342bbaf4d7a51a8a9a40ea152e246/docs/static/search.js
function debounce(func, wait) {
var timeout;
return function () {
var context = this;
var args = arguments;
clearTimeout(timeout);
timeout = setTimeout(function () {
timeout = null;
func.apply(context, args);
}, wait);
};
}
// Taken from mdbook
// The strategy is as follows:
// First, assign a value to each word in the document:
// Words that correspond to search terms (stemmer aware): 40
// Normal words: 2
// First word in a sentence: 8
// Then use a sliding window with a constant number of words and count the
// sum of the values of the words within the window. Then use the window that got the
// maximum sum. If there are multiple maximas, then get the last one.
// Enclose the terms in <b>.
function makeTeaser(body, terms) {
var TERM_WEIGHT = 40;
var NORMAL_WORD_WEIGHT = 2;
var FIRST_WORD_WEIGHT = 8;
var TEASER_MAX_WORDS = 30;
var stemmedTerms = terms.map(function (w) {
return elasticlunr.stemmer(w.toLowerCase());
});
var termFound = false;
var index = 0;
var weighted = []; // contains elements of ["word", weight, index_in_document]
// split in sentences, then words
var sentences = body.toLowerCase().split(". ");
for (var i in sentences) {
var words = sentences[i].split(" ");
var value = FIRST_WORD_WEIGHT;
for (var j in words) {
var word = words[j];
if (word.length > 0) {
for (var k in stemmedTerms) {
if (elasticlunr.stemmer(word).startsWith(stemmedTerms[k])) {
value = TERM_WEIGHT;
termFound = true;
}
}
weighted.push([word, value, index]);
value = NORMAL_WORD_WEIGHT;
}
index += word.length;
index += 1; // ' ' or '.' if last word in sentence
}
index += 1; // because we split at a two-char boundary '. '
}
if (weighted.length === 0) {
return body;
}
var windowWeights = [];
var windowSize = Math.min(weighted.length, TEASER_MAX_WORDS);
// We add a window with all the weights first
var curSum = 0;
for (var i = 0; i < windowSize; i++) {
curSum += weighted[i][1];
}
windowWeights.push(curSum);
for (var i = 0; i < weighted.length - windowSize; i++) {
curSum -= weighted[i][1];
curSum += weighted[i + windowSize][1];
windowWeights.push(curSum);
}
// If we didn't find the term, just pick the first window
var maxSumIndex = 0;
if (termFound) {
var maxFound = 0;
// backwards
for (var i = windowWeights.length - 1; i >= 0; i--) {
if (windowWeights[i] > maxFound) {
maxFound = windowWeights[i];
maxSumIndex = i;
}
}
}
var teaser = [];
var startIndex = weighted[maxSumIndex][2];
for (var i = maxSumIndex; i < maxSumIndex + windowSize; i++) {
var word = weighted[i];
if (startIndex < word[2]) {
// missing text from index to start of `word`
teaser.push(body.substring(startIndex, word[2]));
startIndex = word[2];
}
// add <strong> around search terms
if (word[1] === TERM_WEIGHT) {
teaser.push("<strong>");
}
startIndex = word[2] + word[0].length;
teaser.push(body.substring(word[2], startIndex));
if (word[1] === TERM_WEIGHT) {
teaser.push("</strong>");
}
}
teaser.push("…");
return teaser.join("");
}
function formatSearchResultItem(item, terms) {
return '<div class="item">'
+ `<a href="${item.ref}">${item.doc.title}</a>`
+ `<span>${makeTeaser(item.doc.body, terms)}</span>`
+ '</div>';
}
function initSearch() {
var searchBar = document.getElementById("search-bar");
var searchContainer = document.getElementById("search-container");
var searchResults = document.getElementById("search-results");
var MAX_ITEMS = 10;
var options = {
bool: "AND",
fields: {
title: { boost: 2 },
body: { boost: 1 },
}
};
var currentTerm = "";
var index;
var initIndex = async function () {
if (index === undefined) {
let searchIndex = document.getElementById("search-index").textContent;
index = fetch(searchIndex)
.then(
async function (response) {
return await elasticlunr.Index.load(await response.json());
}
);
}
let res = await index;
return res;
}
searchBar.addEventListener("keyup", debounce(async function () {
var term = searchBar.value.trim();
if (term === currentTerm) {
return;
}
searchResults.style.display = term === "" ? "none" : "flex";
searchResults.innerHTML = "";
currentTerm = term;
if (term === "") {
return;
}
var results = (await initIndex()).search(term, options);
if (results.length === 0) {
searchResults.style.display = "none";
return;
}
for (var i = 0; i < Math.min(results.length, MAX_ITEMS); i++) {
searchResults.innerHTML += formatSearchResultItem(results[i], term.split(" "));
}
}, 150));
document.addEventListener("keydown", function (event) {
if (event.key === "/") {
event.preventDefault();
toggleSearch();
}
});
document.getElementById("search-toggle").addEventListener("click", toggleSearch);
}
function toggleSearch() {
var searchContainer = document.getElementById("search-container");
var searchBar = document.getElementById("search-bar");
searchContainer.classList.toggle("active");
searchBar.toggleAttribute("disabled");
searchBar.focus();
}
if (document.readyState === "complete" ||
(document.readyState !== "loading" && !document.documentElement.doScroll)
) {
initSearch();
} else {
document.addEventListener("DOMContentLoaded", initSearch);
}

View file

@ -1,127 +0,0 @@
// Based on https://codeberg.org/daudix/duckquill/issues/101#issuecomment-2377169
let searchSetup = false;
let fuse;
async function initIndex() {
if (searchSetup) return;
const url = document.getElementById("search-index").textContent;
const response = await fetch(url);
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
const options = {
includeScore: false,
includeMatches: true,
ignoreLocation: true,
threshold: 0.15,
keys: [
{ name: "title", weight: 3 },
{ name: "description", weight: 2 },
{ name: "body", weight: 1 }
]
};
fuse = new Fuse(await response.json(), options);
searchSetup = true;
console.log("Search index initialized successfully");
}
function toggleSearch() {
initIndex();
const searchBar = document.getElementById("search-bar");
const searchContainer = document.getElementById("search-container");
const searchResults = document.getElementById("search-results");
searchContainer.classList.toggle("active");
searchBar.toggleAttribute("disabled");
searchBar.focus();
}
function debounce(actual_fn, wait) {
let timeoutId;
return (...args) => {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
actual_fn(...args);
}, wait);
};
};
function initSearch() {
const searchBar = document.getElementById("search-bar");
const searchResults = document.getElementById("search-results");
const searchContainer = document.getElementById("search-container");
const MAX_ITEMS = 10;
const MAX_RESULTS = 4;
let currentTerm = "";
searchBar.addEventListener("keyup", (e) => {
const searchVal = searchBar.value.trim();
const results = fuse.search(searchVal, { limit: MAX_ITEMS });
let html = "";
for (const result of results) {
html += makeTeaser(result, searchVal);
}
searchResults.innerHTML = html;
if (html) {
searchResults.style.display = "flex";
} else {
searchResults.style.display = "none";
}
});
function makeTeaser(result, searchVal) {
const TEASER_SIZE = 20;
let output = `<div class="search-result item"><a class="result-title" href=${result.item.url}>${result.item.title}</a>`;
for (const match of result.matches) {
if (match.key === "title") continue;
const indices = match.indices.sort((a, b) => Math.abs(a[1] - a[0] - searchVal.length) - Math.abs(b[1] - b[0] - searchVal.length)).slice(0, MAX_RESULTS);
const value = match.value;
for (const ind of indices) {
const start = Math.max(0, ind[0] - TEASER_SIZE);
const end = Math.min(value.length - 1, ind[1] + TEASER_SIZE);
output += "<span>"
+ value.substring(start, ind[0])
+ `<strong>${value.substring(ind[0], ind[1] + 1)}</strong>`
+ value.substring(ind[1] + 1, end)
+ "</span>";
}
if (match.indices.length > 4) {
const moreMatchesText = document.getElementById("more-matches-text").textContent;
output += `<span class="more-matches">${moreMatchesText}</span>`.replace("$MATCHES", `+${match.indices.length - MAX_RESULTS}`);
}
}
return output + "</div>";
}
/*window.addEventListener("click", function (event) {
if (searchSetup && searchBar.getAttribute("disabled") === null && !searchContainer.contains(event.target)) {
toggleSearch();
}
}, { passive: true });*/
document.addEventListener("keydown", function(event) {
if (event.key === "/") {
event.preventDefault();
toggleSearch();
}
});
document.getElementById("search-toggle").addEventListener("click", toggleSearch);
}
if (document.readyState === "complete" ||
(document.readyState !== "loading" && !document.documentElement.doScroll))
initSearch();
else
document.addEventListener("DOMContentLoaded", initSearch);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,15 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://studio-umzu.de/</loc>
</url>
<url>
<loc>https://studio-umzu.de/de/</loc>
</url>
<url>
<loc>https://studio-umzu.de/de/tags/</loc>
</url>
<url>
<loc>https://studio-umzu.de/tags/</loc>
</url>
</urlset>

File diff suppressed because one or more lines are too long

View file

@ -1,283 +0,0 @@
/*
* theme "Solarized (dark)" generated by syntect
*/
.z-code {
color: #839496;
background-color: #002b36;
}
.z-comment, .z-meta.z-documentation {
color: #586e75;
}
.z-string {
color: #2aa198;
}
.z-string.z-regexp {
color: #2aa198;
}
.z-constant.z-character.z-escape {
color: #dc322f;
}
.z-constant.z-numeric {
color: #6c71c4;
}
.z-variable {
color: #268bd2;
}
.z-variable.z-function {
color: #b58900;
}
.z-variable.z-language {
color: #d33682;
}
.z-keyword {
color: #859900;
}
.z-meta.z-import .z-keyword, .z-keyword.z-control.z-import, .z-keyword.z-control.z-import.z-from, .z-keyword.z-other.z-import, .z-keyword.z-control.z-at-rule.z-include, .z-keyword.z-control.z-at-rule.z-import {
color: #cb4b16;
}
.z-keyword.z-operator.z-comparison, .z-keyword.z-operator.z-assignment, .z-keyword.z-operator.z-arithmetic {
color: #657b83;
}
.z-storage {
color: #859900;
}
.z-storage.z-modifier {
color: #93a1a1;
}
.z-keyword.z-control.z-class, .z-entity.z-name, .z-entity.z-name.z-class, .z-entity.z-name.z-type.z-class {
color: #b58900;
}
.z-entity.z-other.z-inherited-class {
color: #268bd2;
}
.z-entity.z-other.z-attribute-name {
color: #b58900;
}
.z-support, .z-support.z-type, .z-support.z-class {
color: #859900;
}
.z-entity.z-name.z-function {
color: #b58900;
}
.z-punctuation.z-definition.z-variable {
color: #859900;
}
.z-constant, .z-constant.z-language, .z-meta.z-preprocessor {
color: #b58900;
}
.z-entity.z-name.z-section {
color: #cb4b16;
}
.z-support.z-function.z-construct, .z-keyword.z-other.z-new {
color: #dc322f;
}
.z-constant.z-character, .z-constant.z-other {
color: #cb4b16;
}
.z-entity.z-name.z-tag {
color: #268bd2;
}
.z-punctuation.z-definition.z-tag.z-html, .z-punctuation.z-definition.z-tag.z-begin, .z-punctuation.z-definition.z-tag.z-end {
color: #586e75;
}
.z-support.z-function {
color: #859900;
}
.z-punctuation.z-separator.z-continuation {
color: #dc322f;
}
.z-storage.z-type {
color: #268bd2;
}
.z-support.z-type.z-exception {
color: #cb4b16;
}
.z-keyword.z-other.z-special-method {
color: #cb4b16;
}
.z-invalid {
background-color: #6e2e32;
}
.z-string.z-quoted.z-double, .z-string.z-quoted.z-single {
color: #2aa198;
}
.z-punctuation.z-definition.z-string {
color: #839496;
}
.z-meta.z-brace.z-square, .z-punctuation.z-section.z-brackets {
color: #268bd2;
}
.z-meta.z-brace.z-round, .z-meta.z-brace.z-curly, .z-punctuation.z-section, .z-punctuation.z-section.z-block, .z-punctuation.z-definition.z-parameters, .z-punctuation.z-section.z-group {
color: #657b83;
}
.z-support.z-constant.z-color, .z-invalid.z-deprecated.z-color.z-w3c-non-standard-color-name.z-scss {
color: #b58900;
}
.z-meta.z-selector.z-css {
color: #657b83;
}
.z-entity.z-name.z-tag.z-css, .z-entity.z-name.z-tag.z-scss, .z-source.z-less .z-keyword.z-control.z-html.z-elements, .z-source.z-sass .z-keyword.z-control.z-untitled {
color: #b58900;
}
.z-entity.z-other.z-attribute-name.z-class {
color: #b58900;
}
.z-entity.z-other.z-attribute-name.z-id {
color: #b58900;
}
.z-entity.z-other.z-attribute-name.z-pseudo-element, .z-entity.z-other.z-attribute-name.z-tag.z-pseudo-element, .z-entity.z-other.z-attribute-name.z-pseudo-class, .z-entity.z-other.z-attribute-name.z-tag.z-pseudo-class {
color: #268bd2;
}
.z-text.z-html.z-basic .z-meta.z-tag.z-other.z-html, .z-text.z-html.z-basic .z-meta.z-tag.z-any.z-html, .z-text.z-html.z-basic .z-meta.z-tag.z-block.z-any, .z-text.z-html.z-basic .z-meta.z-tag.z-inline.z-any, .z-text.z-html.z-basic .z-meta.z-tag.z-structure.z-any.z-html, .z-text.z-html.z-basic .z-source.z-js.z-embedded.z-html, .z-punctuation.z-separator.z-key-value.z-html {
color: #657b83;
}
.z-text.z-html.z-basic .z-entity.z-other.z-attribute-name.z-html, .z-meta.z-tag.z-xml .z-entity.z-other.z-attribute-name {
color: #b58900;
}
.z-keyword.z-other.z-special-method.z-ruby {
color: #859900;
}
.z-variable.z-other.z-constant.z-ruby {
color: #b58900;
}
.z-constant.z-other.z-symbol.z-ruby {
color: #2aa198;
}
.z-keyword.z-other.z-special-method.z-ruby {
color: #cb4b16;
}
.z-meta.z-array .z-support.z-function.z-construct.z-php {
color: #b58900;
}
.z-entity.z-name.z-function.z-preprocessor.z-c, .z-meta.z-preprocessor.z-c.z-include, .z-meta.z-preprocessor.z-macro.z-c {
color: #cb4b16;
}
.z-meta.z-preprocessor.z-c.z-include .z-string.z-quoted.z-other.z-lt-gt.z-include.z-c, .z-meta.z-preprocessor.z-c.z-include .z-punctuation.z-definition.z-string.z-begin.z-c, .z-meta.z-preprocessor.z-c.z-include .z-punctuation.z-definition.z-string.z-end.z-c {
color: #2aa198;
}
.z-other.z-package.z-exclude, .z-other.z-remove {
color: #dc322f;
}
.z-other.z-add {
color: #2aa198;
}
.z-punctuation.z-section.z-group.z-tex, .z-punctuation.z-definition.z-arguments.z-begin.z-latex, .z-punctuation.z-definition.z-arguments.z-end.z-latex, .z-punctuation.z-definition.z-arguments.z-latex {
color: #dc322f;
}
.z-meta.z-group.z-braces.z-tex {
color: #b58900;
}
.z-string.z-other.z-math.z-tex {
color: #b58900;
}
.z-variable.z-parameter.z-function.z-latex {
color: #cb4b16;
}
.z-punctuation.z-definition.z-constant.z-math.z-tex {
color: #dc322f;
}
.z-text.z-tex.z-latex .z-constant.z-other.z-math.z-tex, .z-constant.z-other.z-general.z-math.z-tex, .z-constant.z-other.z-general.z-math.z-tex, .z-constant.z-character.z-math.z-tex {
color: #2aa198;
}
.z-string.z-other.z-math.z-tex {
color: #b58900;
}
.z-punctuation.z-definition.z-string.z-begin.z-tex, .z-punctuation.z-definition.z-string.z-end.z-tex {
color: #dc322f;
}
.z-keyword.z-control.z-label.z-latex, .z-text.z-tex.z-latex .z-constant.z-other.z-general.z-math.z-tex {
color: #2aa198;
}
.z-variable.z-parameter.z-definition.z-label.z-latex {
color: #dc322f;
}
.z-support.z-function.z-be.z-latex {
color: #859900;
}
.z-support.z-function.z-section.z-latex {
color: #cb4b16;
}
.z-support.z-function.z-general.z-tex {
color: #2aa198;
}
.z-keyword.z-control.z-ref.z-latex {
color: #2aa198;
}
.z-storage.z-type.z-class.z-python, .z-storage.z-type.z-function.z-python, .z-storage.z-modifier.z-global.z-python {
color: #859900;
}
.z-support.z-type.z-exception.z-python {
color: #b58900;
}
.z-meta.z-scope.z-for-in-loop.z-shell, .z-variable.z-other.z-loop.z-shell {
color: #93a1a1;
}
.z-meta.z-scope.z-case-block.z-shell, .z-meta.z-scope.z-case-body.z-shell {
color: #93a1a1;
}
.z-punctuation.z-definition.z-logical-expression.z-shell {
color: #dc322f;
}
.z-storage.z-modifier.z-c++ {
color: #859900;
}
.z-support.z-function.z-perl {
color: #268bd2;
}
.z-meta.z-diff, .z-meta.z-diff.z-header {
color: #586e75;
}
.z-meta.z-diff.z-range {
color: #268bd2;
}
.z-markup.z-deleted {
color: #dc322f;
}
.z-markup.z-changed {
color: #b58900;
}
.z-markup.z-inserted {
color: #859900;
}
.z-markup.z-warning {
color: #b58900;
}
.z-markup.z-error {
color: #dc322f;
}
.z-markup.z-heading, .z-punctuation.z-definition.z-heading.z-markdown {
color: #b58900;
font-weight: bold;
}
.z-markup.z-quote {
color: #859900;
}
.z-markup.z-italic {
font-style: italic;
}
.z-markup.z-bold {
font-weight: bold;
}
.z-markup.z-underline.z-link.z-markdown, .z-meta.z-link.z-reference .z-constant.z-other.z-reference.z-link.z-markdown {
color: #2aa198;
}
.z-constant.z-other.z-reference.z-link.z-markdown {
color: #6c71c4;
}
.z-meta.z-paragraph.z-markdown .z-meta.z-dummy.z-line-break {
background-color: #586e75;
}
.z-brackethighlighter.z-all {
color: #586e75;
}
.z-entity.z-name.z-filename.z-find-in-files {
color: #2aa198;
}
.z-constant.z-numeric.z-line-number.z-find-in-files {
color: #586e75;
}
.z-variable.z-other.z-readwrite.z-js, .z-variable.z-other.z-object.z-js, .z-variable.z-other.z-constant.z-js {
color: #839496;
}

View file

@ -1,283 +0,0 @@
/*
* theme "Solarized (light)" generated by syntect
*/
.z-code {
color: #657b83;
background-color: #fdf6e3;
}
.z-comment, .z-meta.z-documentation {
color: #93a1a1;
}
.z-string {
color: #2aa198;
}
.z-string.z-regexp {
color: #2aa198;
}
.z-constant.z-character.z-escape {
color: #dc322f;
}
.z-constant.z-numeric {
color: #6c71c4;
}
.z-variable {
color: #268bd2;
}
.z-variable.z-function {
color: #b58900;
}
.z-variable.z-language {
color: #d33682;
}
.z-keyword {
color: #859900;
}
.z-meta.z-import .z-keyword, .z-keyword.z-control.z-import, .z-keyword.z-control.z-import.z-from, .z-keyword.z-other.z-import, .z-keyword.z-control.z-at-rule.z-include, .z-keyword.z-control.z-at-rule.z-import {
color: #cb4b16;
}
.z-keyword.z-operator.z-comparison, .z-keyword.z-operator.z-assignment, .z-keyword.z-operator.z-arithmetic {
color: #657b83;
}
.z-storage {
color: #859900;
}
.z-storage.z-modifier {
color: #586e75;
}
.z-keyword.z-control.z-class, .z-entity.z-name, .z-entity.z-name.z-class, .z-entity.z-name.z-type.z-class {
color: #b58900;
}
.z-entity.z-other.z-inherited-class {
color: #268bd2;
}
.z-entity.z-other.z-attribute-name {
color: #b58900;
}
.z-support, .z-support.z-type, .z-support.z-class {
color: #859900;
}
.z-entity.z-name.z-function {
color: #b58900;
}
.z-punctuation.z-definition.z-variable {
color: #859900;
}
.z-constant, .z-constant.z-language, .z-meta.z-preprocessor {
color: #b58900;
}
.z-entity.z-name.z-section {
color: #cb4b16;
}
.z-support.z-function.z-construct, .z-keyword.z-other.z-new {
color: #dc322f;
}
.z-constant.z-character, .z-constant.z-other {
color: #cb4b16;
}
.z-entity.z-name.z-tag {
color: #268bd2;
}
.z-punctuation.z-definition.z-tag.z-html, .z-punctuation.z-definition.z-tag.z-begin, .z-punctuation.z-definition.z-tag.z-end {
color: #93a1a1;
}
.z-support.z-function {
color: #859900;
}
.z-punctuation.z-separator.z-continuation {
color: #dc322f;
}
.z-storage.z-type {
color: #268bd2;
}
.z-support.z-type.z-exception {
color: #cb4b16;
}
.z-keyword.z-other.z-special-method {
color: #cb4b16;
}
.z-invalid {
background-color: #ec9489;
}
.z-string.z-quoted.z-double, .z-string.z-quoted.z-single {
color: #2aa198;
}
.z-punctuation.z-definition.z-string {
color: #839496;
}
.z-meta.z-brace.z-square, .z-punctuation.z-section.z-brackets {
color: #268bd2;
}
.z-meta.z-brace.z-round, .z-meta.z-brace.z-curly, .z-punctuation.z-section, .z-punctuation.z-section.z-block, .z-punctuation.z-definition.z-parameters, .z-punctuation.z-section.z-group {
color: #657b83;
}
.z-support.z-constant.z-color, .z-invalid.z-deprecated.z-color.z-w3c-non-standard-color-name.z-scss {
color: #b58900;
}
.z-meta.z-selector.z-css {
color: #657b83;
}
.z-entity.z-name.z-tag.z-css, .z-entity.z-name.z-tag.z-scss, .z-source.z-less .z-keyword.z-control.z-html.z-elements, .z-source.z-sass .z-keyword.z-control.z-untitled {
color: #b58900;
}
.z-entity.z-other.z-attribute-name.z-class {
color: #b58900;
}
.z-entity.z-other.z-attribute-name.z-id {
color: #b58900;
}
.z-entity.z-other.z-attribute-name.z-pseudo-element, .z-entity.z-other.z-attribute-name.z-tag.z-pseudo-element, .z-entity.z-other.z-attribute-name.z-pseudo-class, .z-entity.z-other.z-attribute-name.z-tag.z-pseudo-class {
color: #268bd2;
}
.z-text.z-html.z-basic .z-meta.z-tag.z-other.z-html, .z-text.z-html.z-basic .z-meta.z-tag.z-any.z-html, .z-text.z-html.z-basic .z-meta.z-tag.z-block.z-any, .z-text.z-html.z-basic .z-meta.z-tag.z-inline.z-any, .z-text.z-html.z-basic .z-meta.z-tag.z-structure.z-any.z-html, .z-text.z-html.z-basic .z-source.z-js.z-embedded.z-html, .z-punctuation.z-separator.z-key-value.z-html {
color: #657b83;
}
.z-text.z-html.z-basic .z-entity.z-other.z-attribute-name.z-html, .z-meta.z-tag.z-xml .z-entity.z-other.z-attribute-name {
color: #b58900;
}
.z-keyword.z-other.z-special-method.z-ruby {
color: #859900;
}
.z-variable.z-other.z-constant.z-ruby {
color: #b58900;
}
.z-constant.z-other.z-symbol.z-ruby {
color: #2aa198;
}
.z-keyword.z-other.z-special-method.z-ruby {
color: #cb4b16;
}
.z-meta.z-array .z-support.z-function.z-construct.z-php {
color: #b58900;
}
.z-entity.z-name.z-function.z-preprocessor.z-c, .z-meta.z-preprocessor.z-c.z-include, .z-meta.z-preprocessor.z-macro.z-c {
color: #cb4b16;
}
.z-meta.z-preprocessor.z-c.z-include .z-string.z-quoted.z-other.z-lt-gt.z-include.z-c, .z-meta.z-preprocessor.z-c.z-include .z-punctuation.z-definition.z-string.z-begin.z-c, .z-meta.z-preprocessor.z-c.z-include .z-punctuation.z-definition.z-string.z-end.z-c {
color: #2aa198;
}
.z-other.z-package.z-exclude, .z-other.z-remove {
color: #dc322f;
}
.z-other.z-add {
color: #2aa198;
}
.z-punctuation.z-section.z-group.z-tex, .z-punctuation.z-definition.z-arguments.z-begin.z-latex, .z-punctuation.z-definition.z-arguments.z-end.z-latex, .z-punctuation.z-definition.z-arguments.z-latex {
color: #dc322f;
}
.z-meta.z-group.z-braces.z-tex {
color: #b58900;
}
.z-string.z-other.z-math.z-tex {
color: #b58900;
}
.z-variable.z-parameter.z-function.z-latex {
color: #cb4b16;
}
.z-punctuation.z-definition.z-constant.z-math.z-tex {
color: #dc322f;
}
.z-text.z-tex.z-latex .z-constant.z-other.z-math.z-tex, .z-constant.z-other.z-general.z-math.z-tex, .z-constant.z-other.z-general.z-math.z-tex, .z-constant.z-character.z-math.z-tex {
color: #2aa198;
}
.z-string.z-other.z-math.z-tex {
color: #b58900;
}
.z-punctuation.z-definition.z-string.z-begin.z-tex, .z-punctuation.z-definition.z-string.z-end.z-tex {
color: #dc322f;
}
.z-keyword.z-control.z-label.z-latex, .z-text.z-tex.z-latex .z-constant.z-other.z-general.z-math.z-tex {
color: #2aa198;
}
.z-variable.z-parameter.z-definition.z-label.z-latex {
color: #dc322f;
}
.z-support.z-function.z-be.z-latex {
color: #859900;
}
.z-support.z-function.z-section.z-latex {
color: #cb4b16;
}
.z-support.z-function.z-general.z-tex {
color: #2aa198;
}
.z-keyword.z-control.z-ref.z-latex {
color: #2aa198;
}
.z-storage.z-type.z-class.z-python, .z-storage.z-type.z-function.z-python, .z-storage.z-modifier.z-global.z-python {
color: #859900;
}
.z-support.z-type.z-exception.z-python {
color: #b58900;
}
.z-meta.z-scope.z-for-in-loop.z-shell, .z-variable.z-other.z-loop.z-shell {
color: #586e75;
}
.z-meta.z-scope.z-case-block.z-shell, .z-meta.z-scope.z-case-body.z-shell {
color: #586e75;
}
.z-punctuation.z-definition.z-logical-expression.z-shell {
color: #dc322f;
}
.z-storage.z-modifier.z-c++ {
color: #859900;
}
.z-support.z-function.z-perl {
color: #268bd2;
}
.z-meta.z-diff, .z-meta.z-diff.z-header {
color: #93a1a1;
}
.z-meta.z-diff.z-range {
color: #268bd2;
}
.z-markup.z-deleted {
color: #dc322f;
}
.z-markup.z-changed {
color: #b58900;
}
.z-markup.z-inserted {
color: #859900;
}
.z-markup.z-warning {
color: #b58900;
}
.z-markup.z-error {
color: #dc322f;
}
.z-markup.z-heading, .z-punctuation.z-definition.z-heading.z-markdown {
color: #b58900;
font-weight: bold;
}
.z-markup.z-quote {
color: #859900;
}
.z-markup.z-italic {
font-style: italic;
}
.z-markup.z-bold {
font-weight: bold;
}
.z-markup.z-underline.z-link.z-markdown, .z-meta.z-link.z-reference .z-constant.z-other.z-reference.z-link.z-markdown {
color: #2aa198;
}
.z-constant.z-other.z-reference.z-link.z-markdown {
color: #6c71c4;
}
.z-meta.z-paragraph.z-markdown .z-meta.z-dummy.z-line-break {
background-color: #eee8d5;
}
.z-brackethighlighter.z-all {
color: #93a1a1;
}
.z-entity.z-name.z-filename.z-find-in-files {
color: #2aa198;
}
.z-constant.z-numeric.z-line-number.z-find-in-files {
color: #93a1a1;
}
.z-variable.z-other.z-readwrite.z-js, .z-variable.z-other.z-object.z-js, .z-variable.z-other.z-constant.z-js {
color: #657b83;
}

View file

@ -1,104 +0,0 @@
// Theme Initialization
(function () {
// Get the default theme from the HTML data-theme attribute.
const defaultTheme = document.documentElement.getAttribute("data-theme");
// Set the data-default-theme attribute only if defaultTheme is not null.
if (defaultTheme) {
document.documentElement.setAttribute("data-default-theme", defaultTheme);
}
// Attempt to retrieve the current theme from the browser's local storage.
const storedTheme = localStorage.getItem("theme");
if (storedTheme && storedTheme !== "system") {
document.documentElement.setAttribute("data-theme", storedTheme);
} else if (defaultTheme && storedTheme !== "system") {
document.documentElement.setAttribute("data-theme", defaultTheme);
} else {
// If no theme is found in local storage and no default theme is set, hand over control to the CSS.
document.documentElement.removeAttribute("data-theme");
}
// Expose defaultTheme to the outer scope.
window.defaultTheme = defaultTheme;
})();
// Icon Update and Theme Switching
function setTheme(theme, saveToLocalStorage = false) {
if (theme === "system") {
document.documentElement.removeAttribute("data-theme");
} else {
document.documentElement.setAttribute("data-theme", theme);
}
if (saveToLocalStorage) {
localStorage.setItem("theme", theme);
} else {
localStorage.removeItem("theme");
}
// Update icon class based on the selected theme.
updateIconClass(theme);
// Update the active button based on the selected theme.
updateActiveButton(theme);
}
function resetTheme() {
// Reset the theme to the default or system preference if no default is set.
setTheme(window.defaultTheme || "system");
}
function switchTheme(theme) {
if (theme === "system") {
resetTheme();
} else {
setTheme(theme, true);
}
}
function updateIconClass(theme) {
const iconElement = document.querySelector("#theme-switcher summary .icon");
// Remove any existing theme classes
iconElement.classList.remove("light", "dark");
// Add the appropriate class based on the selected theme
if (theme === "light") {
iconElement.classList.add("light");
} else if (theme === "dark") {
iconElement.classList.add("dark");
}
}
function updateActiveButton(theme) {
// Remove .active class from all buttons
document.querySelectorAll('#theme-switcher button').forEach(button => {
button.classList.remove('active');
});
// Add .active class to the button corresponding to the current theme
const activeButton = document.querySelector(`#theme-${theme}`);
if (activeButton) {
activeButton.classList.add('active');
}
}
document.getElementById("theme-light").addEventListener("click", function () {
switchTheme("light");
});
document.getElementById("theme-dark").addEventListener("click", function () {
switchTheme("dark");
});
document.getElementById("theme-system").addEventListener("click", function () {
switchTheme("system");
});
// Update icon class on page load based on current theme
const currentTheme = localStorage.getItem("theme") || window.defaultTheme || "system";
updateIconClass(currentTheme);
updateActiveButton(currentTheme);
// Make the switchTheme function accessible globally
window.switchTheme = switchTheme;