diff --git a/Devvlogs/Your Simulation/index.html b/Devvlogs/Your Simulation/index.html deleted file mode 100644 index 5053d96..0000000 --- a/Devvlogs/Your Simulation/index.html +++ /dev/null @@ -1,374 +0,0 @@ - - - - - - BR0TCRAFT - - - -
- -

BR0TCRAFT

-
-
-
- -

Gamename

-
- - - - -

Datum

-
Update: 12.10.2024
- - - - -

ein Button zum Interaktieren

-
-
- Spielen -
-
- - - -

ein kleiner Button zum Interaktieren

-
-
- Spielen -
-
- - - - -
Unter Überschrift:
- - - - -
-

Eine simple zwischennotiz

-
- - - - -

Karusell zur Bilder implementierung

- - - - -
- -
-

Noch eine Kategorie für weiteres Datum

-
- - - - - - - - - - - diff --git a/Example-site/index.html b/Example-site/index.html deleted file mode 100644 index 177e6ff..0000000 --- a/Example-site/index.html +++ /dev/null @@ -1,57 +0,0 @@ - - - - - - Br0tcraft - - - -
-
-
-
-
- - BR0TCRAFT -
- -
-
-
-
- -
-
- BR0TCRAFT -


- Spiele - Tutorials - Turbowarp-Extensions - Kontakt - funny-Seiten -
-

Example

- Dies wird genutzt für weitere Websiten als Template - - - - - - \ No newline at end of file diff --git a/Example-site/style.css b/Example-site/style.css deleted file mode 100644 index 18aa16e..0000000 --- a/Example-site/style.css +++ /dev/null @@ -1,172 +0,0 @@ -@font-face { - font-family: "Roboto"; - src: url("/fonts/Roboto/Roboto-Regular.ttf"); -} - -@font-face { - font-family: "Orbitron"; - src: url("/fonts/Orbitron/Orbitron-Black.ttf"); -} - -body { - margin: 0; - font-family: "Orbitron"; - background-color: white; - z-index: 0; -} - -.Leiste { - top: 0px; - left: 0px; - width: 100%; - height: 7vw; - position: fixed; - background-color: rgb(77, 0, 0); - z-index: 0; -} -.Background-Leiste { - top: 0px; - left: 0px; - width: 100%; - height: 7vw; - object-fit: cover; - background-color: transparent; -} - -.header-container { - top: 3vw; - position: relative; - width: 100%; -} - -.container { - position: fixed; - right: 32px; - left: 10px; - top: 0px; - font-family: Orbitron; - font-size: 24px; - display: flex; - align-items: center; - justify-content: space-between; -} - -.menu-link-home { - color: white; - text-decoration: none; - margin-left: 0px; - font-size: 6vw; - -} - -.menu-link { - font-size: 1.5vw; - color: white; - text-decoration: none; - margin-left: 16px; -} - -.menu-link:hover { - text-decoration: underline; -} - -.headline { - text-align: center; - color: rgb(0, 0, 0); - font-size: 4vw; - text-shadow: 5px 5px 3px rgba(5, 0, 0, 0.26); -} - -.mobile-menu-icon { - display: none; /* Standardmäßig ausblenden */ - cursor: pointer; - position: fixed; - top: 0.5vw; - right: 20px; -} - -.hamburger-icon { - width: 30px; - height: 3px; - background-color: white; - margin: 6px 0; -} - -.menu-links { - display: flex; - align-items: center; - justify-content: flex-end; /* Rechts ausrichten */ -} - -.menu-links.show { - display: flex; -} - -.menu { - display: none; - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - background-color: #333; /* Hintergrundfarbe des Menüs */ - z-index: 1000; -} - -.menu-link-mobile { - color: white; - padding: 20px; - text-align: center; - text-decoration: none; - display: block; - font-size: 250%; - margin: 20px; /* Abstand zwischen den Links hinzufügen */ -} - -.mobile-menu { - display: none; - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - background-color: transparent; - z-index: 1000; -} - -.Inhalt { - height: 100%; - width: 100%; - top: 10vw; - left: 0px; -} - -@media screen and (max-width: 937px) { - .mobile-menu-icon { - display: block; /* Bei kleinen Bildschirmen anzeigen */ - } - - .hamburger-icon { - display: block; - } - - .menu-links { - display: none; - flex-direction: column; - position: absolute; - top: 80px; - right: 0; /* Rechts ausrichten */ - background-color: #333; - width: 100%; - justify-content: flex-start; /* Links oben ausrichten */ - } - - .menu-links.show { - display: flex; - } - - .mobile-menu.show { - display: block; - background-color: rgb(104, 0, 0); - } -} \ No newline at end of file diff --git a/README.md b/README.md deleted file mode 100644 index 39a2eba..0000000 --- a/README.md +++ /dev/null @@ -1 +0,0 @@ -A simple HTML/JS/CSS starter template \ No newline at end of file diff --git a/about.html b/about.html new file mode 100644 index 0000000..4c565f0 --- /dev/null +++ b/about.html @@ -0,0 +1,309 @@ + + + + + + + BR0TCRAFT | About + + + + + + + + + +
+
+

About me

+ +
+

+ Für mich ist Programmieren wie ein aufgeräumter Schreibtisch: Es arbeitet sich einfach besser, wenn die + Struktur + stimmt. Während ich Python nutze, um Ideen schnell und verständlich in Prototypen zu verwandeln, reizt mich an + C++ die Nähe zur Hardware. Es ist für mich der Schlüssel, um nicht nur Code zu schreiben, sondern auch den + Computer dahinter besser zu begreifen. +

+ Aktuell probiere ich mich viel in der 3DS-Homebrew-Szene aus. Dabei liegt mein Fokus nicht auf maximaler + High-End- + Performance, sondern auf Handwerk: Ich achte auf Cache-Effizienz bei Iterationen, ein sauberes Error-Handling + und + eine Logik, die auch nach Monaten noch nachvollziehbar ist. Ich optimiere lieber Daten im Vorfeld, statt + später + Ladezeiten zu flicken. +

+ In der Open-Source-Welt trage ich dort bei, wo mich die Themen persönlich packen. Ich muss nicht das nächste + große Framework erfinden, mir geht es darum, sinnvolle Verbesserungen beizusteuern und durch eigene + Nischenprojekte zu lernen. Am Ende ist jedes Tool für mich ein Erfolg, wenn es ein echtes Problem löst und + sich der Code dabei „richtig“ anfühlt. +

+
+ +
+
+

Journey

+

Was ich bisher so ausprobiert hatte +

+ +
+ +
+
+
+
Anfang
+

Scratch

+

Wie wahrscheinlich viele andere auch. Logik verstehen, Konzepte ausprobieren, ohne dass ein Compiler + sich + beschwert. Es war für mich ein wohl sinnvoller Einstieg in die Welt der Programmierung.

+ Scratch +
+
+ +
+
+
+
Erstes echtes Projekt
+

Unity & C#

+

Mein erstes richtiges Projekt war ein Mobile Game, das ich tatsächlich bis zum Release gebracht habe. + Es + war zwar nicht das innovativste Spiel der Welt, aber meine Freunde haben es gespielt und es hat mir + bewiesen, dass ich Projekte zu Ende bringen kann. C# und Unity waren ein guter Einstieg, aber nach dem + Projekt wusste ich, das ich tiefer in die Softwareentwicklung eintauchen will. Reinen + Spielelogik lag mir leider nicht so.

+ C# + Unity +
+
+ +
+
+
+
Alternative
+

Godot ausprobiert

+

Schnellere Iterationen, leichtgewichtiger und GDScript war herrlich unkompliziert. Als Unity Ende 2023 + die Lizenzpolitik änderte, fiel der Wechsel leicht. Auch wenn hier kein fertiges Spiel entstanden ist, + habe ich bei den Experimenten extrem viel über Architektur und sauberen Code gelernt.

+ GDScript + Godot +
+
+ +
+
+
+
Parallel
+

Python & JavaScript

+

Python nutze ich vor allem für Backends und Automatisierungen, es ist nach wie vor meine erste Wahl, + wenn es darum geht, schnell produktiv zu sein. JavaScript war ein kurzer Ausflug, nicht unbedingt meine + Lieblingssprache, aber ein notwendiges und brauchbares Werkzeug für alles, was im Browser stattfindet. +

+ Python + JavaScript +
+
+ +
+
+
+
Aktuell
+

C++

+

Hier bin ich momentan. Und das nicht weil es einfach ist, sondern weil ich wissen will, was unter + der Oberfläche passiert. C++ ist oft frustrierend, aber genau deshalb so lehrreich.

+ C++ +
+
+ +
+
+
+ + + + \ No newline at end of file diff --git a/apps/Br0tbot/index.html b/apps/Br0tbot/index.html deleted file mode 100644 index 511bd46..0000000 --- a/apps/Br0tbot/index.html +++ /dev/null @@ -1,346 +0,0 @@ - - - - - - BR0TCRAFT - - - -
- -

BR0TCRAFT

-
-
-
-
-
Br0tbot (in Entwicklung, kann noch nicht alles)
-
-
-
- App 1 Image -
-
- -
-
-

- Dies ist der Python-Code für einen Discord-Bot, der als vielseitiger Allrounder entwickelt wurde. Der Bot beherrscht die wichtigsten Funktionen, die man von einem Discord-Bot erwartet. Dazu gehören beispielsweise das Akzeptieren von Regeln, das Begrüßen und Verabschieden von Mitgliedern, das Zuweisen von Rollen, das Erstellen von Tickets, ein Levelsystem, die Kontrolle des Servers sowie das Erstellen von Statistiken. -

- -

- Zusätzlich gibt es auch unterhaltsame Funktionen zur Zeitvertreibung, wie etwa einen "Counting"-Kanal, einen "Coins"-Kanal und vieles mehr. Über den Button "Spielen" gelangt man zu einer Seite, auf der der Bot eingerichtet werden kann. -

- -

- Der Bot ist Open Source und für das Selbst-Hosting gedacht. -

-

- Ps: Der Bot ist noch in Entwicklung und es stehen evtl noch nicht alle versprochenen funktionen zur Verfügunng -

-
- -
- - - - - - - - - - - diff --git a/apps/Br0tkabeln/index.html b/apps/Br0tkabeln/index.html deleted file mode 100644 index 735b67d..0000000 --- a/apps/Br0tkabeln/index.html +++ /dev/null @@ -1,345 +0,0 @@ - - - - - - BR0TCRAFT - - - - -
-
-
Br0tkabeln (beta)
-
-
-
- App 1 Image -
-
- -
-
-

- Ein simpler und hoffentlich auch einfach zu verstehender Vokabeltrainer -

- -

- Der Vokabeltrainer wurde mit Python, Html, Css, Javascript und Flask erstellt. Deine Vokabeln werden kostenlos auf dem Server gespeichert -

- -

- Es werden jediglich Benutzername, Vokabeln(English & Deutsch) und deren Level in Klartext auf dem Server gespeichert. Das Passwort wiederum wird zur Sicherheit gehasht. -

-

- Es gehen keine Daten an Dritte weiter und nur ich (und der jeweilge Besitzer seiner bzw mit ihm geteilten Acountdaten) haben Zugriff auf diese oben genannten Informationen -

-
- -
- - - - - - - - - - - diff --git a/apps/PocketCode/Sensor-datas/index.html b/apps/PocketCode/Sensor-datas/index.html deleted file mode 100644 index 6e544a1..0000000 --- a/apps/PocketCode/Sensor-datas/index.html +++ /dev/null @@ -1,274 +0,0 @@ -!DOCTYPE html> - - - - - BR0TCRAFT - - - - -
-
-
Sensor data
-
-
-
- App 1 Image -
-
- -
-
-

Diese App ist dazu gedacht Programmteile in sein eigenes Pocket Code Projekt einzufügen. Es hat einfach schon bestimmte komplizierte programme erstellt bei denen es evtl. sinvoll ist diese auch in sein Spiel hinzuzufügen, ohne daran ewig zu arbeiten

- -

Diese App erlaubt dir herauszufinden ob und wie lange die App im Hintergrund lief. Wie lange es her ist das die App das letzte mal geöffnet wurde. Eine automatische Update erkennen für dein Spiel, um zu checken obn das game sich aktualisieren soll und zu guter letzt noch die funktion eine Liste anzuzeigen (praktisch zum debuggen von Spielen). Benutzt diese App wie ihr wollt, sie gehört ganz euch (Lizenz: CC0)

- -

Spielbar nur am Handy mit der App Pocket Code

-
-
- - - - - - - - - diff --git a/apps/PocketCode/Tower-defense/index.html b/apps/PocketCode/Tower-defense/index.html deleted file mode 100644 index 1c926ff..0000000 --- a/apps/PocketCode/Tower-defense/index.html +++ /dev/null @@ -1,334 +0,0 @@ - - - - - - BR0TCRAFT - - - - -
-
-
Tower defense (Beta)
-
-
-
- App 1 Image -
-
- -
-
-

Ein kleines unvertiges Tower-Defense-game, welches Leider überhaupt nicht mehr funktioniert. Immerhin sind die Assets recht schön geworden die kann man/irgendjemand ja noch verwenden(Lizenz: CC0). Ich hab jedenfalls momentan keine Lust daran weiter zu arbeiten

- -

In dem Spiel gibt es Münzen mit denen kannst du dir Verteidigungsposten kaufen bzw Verbessern. Geld bekommst du von den Angriffen von Gegnern die du starten kannst, welche auch immer stärker werden

- -

Spielbar nur am Handy mit der App Pocket Code

-
- -
- - - - - - - - - - - diff --git a/apps/PocketCode/auto-clicker/index.html b/apps/PocketCode/auto-clicker/index.html deleted file mode 100644 index 9c29946..0000000 --- a/apps/PocketCode/auto-clicker/index.html +++ /dev/null @@ -1,334 +0,0 @@ - - - - - - BR0TCRAFT - - - - -
-
-
auto-clicker
-
-
-
- App 1 Image -
-
- -
-
-

Ein meiner Meinung nach sehr einfach zu bedienender auto-clicker

- -

Es ist an sich ganz einfach. Die App besteht nur aus einem ObjeKt, dieses Objekt kopierst du dir und fügst es in das Spiel deiner Wahl ein und schon hast du einen automatischen Klicker immer an deiner Seite

- -

Spielbar nur am Handy mit der App Pocket Code

-
- -
- - - - - - - - - - - diff --git a/apps/Tiny-Snake/index.html b/apps/Tiny-Snake/index.html deleted file mode 100644 index 8c46c0e..0000000 --- a/apps/Tiny-Snake/index.html +++ /dev/null @@ -1,332 +0,0 @@ - - - - - - BR0TCRAFT - - - - -
-
-
Tiny Snake
-
-
-
- App 1 Image -
-
- -
-
-

Hier ist das nächste große AAA-game. Naja nicht wirklich. Also eigentlich ist es ein super einfaches Snake game, ohne vielen funktionen. Das erstaunliche dabei ist das es so kompakt wie (für mich) möglich ist. Funktioniert leider nur am PC. Man bewegt die Schlange mit den Pfeiltasten

-
- -
- - - - - - - - - - - diff --git a/article.html b/article.html new file mode 100644 index 0000000..da46185 --- /dev/null +++ b/article.html @@ -0,0 +1,378 @@ + + + + + + + BR0TCRAFT | Artikel + + + + + + + + + + + + + + + + + +
+
+ + + + + +
+
+

Lade Inhalt...

+
+ +
+ + +
+ +
+
+ + + + + + \ No newline at end of file diff --git a/assets.html b/assets.html new file mode 100644 index 0000000..151e485 --- /dev/null +++ b/assets.html @@ -0,0 +1,290 @@ + + + + + + + BR0TCRAFT | Assets + + + + + + + + + +
+
+

Assets

+ +
+ + + + + CC0 Public Domain +
+ +
+

+ Icons, Grafiken und kleine UI-Assets, die ich für eigene Projekte erstellt oder gesammelt habe. + Alles steht unter CC0, + keine Namensnennung erforderlich, keine Einschränkungen bei der Nutzung. +

+

+ Der Grund ist eigentlich ganz einfach. Ich nutze selbst viele freie Ressourcen anderer Leute. + Durch die Assets möchte ich auch etwas zurückgeben und das so unkompliziert wie möglich. +

+

+ Mitnehmen, anpassen, verwenden. +

+
+ +
+ + + + +
+ +
+ +
+
🔧
+
+
tool.svg
+
SVG · Icon
+
+
+ +
+
⚙️
+
+
settings.svg
+
SVG · Icon
+
+
+ +
+
🎨
+
+
button-set.png
+
PNG · UI
+
+
+ +
+
+
+
noise-dark.png
+
PNG · Textur
+
+
+ +
+
📁
+
+
folder.svg
+
SVG · Icon
+
+
+ +
+
+
+
+
+
progress-bar.svg
+
SVG · UI
+
+
+ +
+ +
+

// Eigene Assets beisteuern?

+

+ Wenn du selbst freie Assets beisteuern willst, meld dich per + Email + oder über GitHub. CC0 ist Bedingung. +

+
+ +
+
+ + + + + + \ No newline at end of file diff --git a/content/3ds/index.json b/content/3ds/index.json new file mode 100644 index 0000000..8e8b1b7 --- /dev/null +++ b/content/3ds/index.json @@ -0,0 +1,25 @@ +{ + "title": "3ds Homebrew Dev Notes", + "chapters": [ + { + "title": "Introduction", + "file": "intro.md", + "id": "intro" + }, + { + "title": "DevkitPro Setup", + "file": "devkitpro.md", + "id": "devkitpro" + }, + { + "title": "Specs", + "children": [ + { + "title": "RAM Allocation", + "file": "specs/ram-allocation.md", + "id": "ram-allocation" + } + ] + } + ] +} \ No newline at end of file diff --git a/content/3ds/intro copy.md b/content/3ds/intro copy.md new file mode 100644 index 0000000..78ba68f --- /dev/null +++ b/content/3ds/intro copy.md @@ -0,0 +1,32 @@ +## A little overview about 3ds specs that should be known when working on homebrew for it + +### Hardware +#### New 3DS +(New 3DS, New 3DS XL, New 2DS XL) +CPU + ARM11 @ 804 MHz (Quad-core) +GPU + Custom PICA200 @ 268 MHz +RAM + 256 MB (more infos here: ) +VRAM + 10 MB +Display + Top: 400x240 + Bottom: 320x240 + +#### Old 3DS +(Old 3DS, Old 3DS XL, Old 2DS) +CPU + ARM11 @ 268 MHz (Dual-core) +GPU + Custom PICA200 @ 134 MHz +RAM + 128 MB +VRAM + 10 MB +Display + Top: 400x240 + Bottom: 320x240 + +### Homebrew \ No newline at end of file diff --git a/content/3ds/intro.md b/content/3ds/intro.md new file mode 100644 index 0000000..51e2896 --- /dev/null +++ b/content/3ds/intro.md @@ -0,0 +1,30 @@ +# 3DS Homebrew Development Wiki & Lookup Table +Welcome to the ultimate resource for Nintendo 3DS homebrew development using devkitPro. This guide covers everything from setting up your toolchain to advanced hardware specifications and CIA deployment. + +🗺️ Documentation Roadmap +1. Toolchain & Environment Setup +Part 1: Installing devkitPro — Step-by-step installation for Windows, Arch Linux, and Debian using the dkp-pacman package manager. + +Part 2: Modern VS Code Integration — Configuring C/C++ IntelliSense, include paths, and automated build tasks for seamless auto-completion. + +2. Core Development & Compilation +Part 3: Anatomy of a 3DS Makefile — Customizing your application's metadata (Name, Author, Description, and Icon). + +Part 4: The Build Pipeline (.3dsx) — Understanding how source code becomes an executable homebrew file. + +3. OS Services & Hardware Interfacing +Part 5: Storage & File Systems — Working with sdmc:/ (SD Card) and compiling assets into romfs:/. + +Part 6: System APIs & Services — Interfacing with the OS: Battery status, System Clock, Notifications, and Mii data. + +Part 7: Networking & Threads — Implementing multi-threading via libctru and managing Wi-Fi/Network sockets. + +4. Advanced Deployment & Assets +Part 8: Creating .cia Files — Packaging your app for direct installation onto the 3DS Home Menu. + +Part 9: Crafting App Banners (2D & 3D) — Designing and compiling custom 2D icons and 3D top-screen banners. + +5. Hardware Architecture & Technical Specifications +Part 10: Memory (RAM) Restrictions — Navigating the strict memory layouts of Old 3DS vs. New 3DS. + +Part 11: CPU & GPU Architecture — Understanding the ARM11/PICA200 limits and graphic rendering options. \ No newline at end of file diff --git a/content/3ds/specs/ram-allocation.md b/content/3ds/specs/ram-allocation.md new file mode 100644 index 0000000..31607ce --- /dev/null +++ b/content/3ds/specs/ram-allocation.md @@ -0,0 +1,26 @@ +# RAM Allocation Guide + +When developing homebrew for the Nintendo 3DS, available RAM depends heavily on two factors: how your application is packaged (.3dsx vs. .cia) and the console hardware (Old 3DS vs. New 3DS). + +## 1. Homebrew Launcher (.3dsx) vs. Installed CIA (.cia) + +### .3dsx (Launched via Homebrew Launcher) +When launching a .3dsx file, your application inherits the memory constraints of the host application used to exploit the system (usually the Nintendo 3DS Sound app or the internet browser via safehax). +* **Available RAM**: You are generally limited to roughly 32 MB to 64 MB of linear memory, as the system allocates the rest to the operating system and the host applet. +* **Limitation**: You cannot request extended memory modes or access New 3DS-exclusive RAM via a .3dsx file. + +### .cia (Installed to Home Menu) +An installed .cia acts like a native retail game. It uses a Result Spec File (.rsf) during compilation to explicitly tell the 3DS OS how much memory to allocate and which CPU mode to use. + +## 2. Memory Modes & .rsf Flags (Old 3DS vs. New 3DS) + +The Old 3DS has 128 MB of total RAM, while the New 3DS has 256 MB. How you configure your .rsf file determines how much of this memory your app can touch. + +| Memory Mode | Available RAM | O3DS Behavior | N3DS Behavior | +|-------------|---------------|---------------|---------------| +| **Application (Default)** | **64 MB** | Standard mode. System applets (Friends list, Browser) remain suspended in the background. Quick launch/exit. | Standard mode. Works identically to O3DS. | +| **SystemMode32** | **32 MB** | Limits the app to 32 MB. Historically used by simple tools to slightly decrease boot times. | Works identically, but rarely used for homebrew. | +| **SystemMode4 (Extended Memory)** | **80 MB** | **Requires App Reboot**: The OS terminates background applets to free up an extra 16 MB. Exiting to the Home Menu forces a console soft-reboot. | **No Reboot Needed**: Because the N3DS has 256 MB of RAM, it can grant 80 MB easily without closing background applets. | +| **SystemMode5 (Extended Memory)** | **96 MB** | **Requires App Reboot**: Used by late-generation heavy games (e.g., Super Smash Bros.). Frees up 32 MB by completely shutting down the Home Menu/OS applets. Exiting forces a long soft-reboot. | **No Reboot Needed**: The N3DS handles this natively without needing a soft-reboot. | + +**Example of Extended Memory Usage:** Super Smash Bros. 3DS and Monster Hunter 4 Ultimate use SystemMode5 (96MB) on an Old 3DS, causing the famous "soft-reboot" when you close them. \ No newline at end of file diff --git a/content/demo-doc/01-intro.md b/content/demo-doc/01-intro.md new file mode 100644 index 0000000..1549168 --- /dev/null +++ b/content/demo-doc/01-intro.md @@ -0,0 +1 @@ +# Einleitung\nDas ist der erste Teil einer strukturierten Dokumentation. diff --git a/content/demo-doc/02-install.md b/content/demo-doc/02-install.md new file mode 100644 index 0000000..8f11b9d --- /dev/null +++ b/content/demo-doc/02-install.md @@ -0,0 +1 @@ +# Installation\nHier steht, wie man alles installiert. diff --git a/content/demo-doc/adv/performance.md b/content/demo-doc/adv/performance.md new file mode 100644 index 0000000..556dcd3 --- /dev/null +++ b/content/demo-doc/adv/performance.md @@ -0,0 +1 @@ +# Performance\nOptimierungstipps für Profis. diff --git a/content/demo-doc/index.json b/content/demo-doc/index.json new file mode 100644 index 0000000..e66f82f --- /dev/null +++ b/content/demo-doc/index.json @@ -0,0 +1,13 @@ +{ + "title": "Struktur-Demo", + "chapters": [ + { "title": "Einleitung", "file": "01-intro.md", "id": "intro" }, + { "title": "Installation", "file": "02-install.md", "id": "install" }, + { + "title": "Fortgeschritten", + "children": [ + { "title": "Performance", "file": "adv/performance.md", "id": "perf" } + ] + } + ] +} diff --git a/enviroments/PocketCode/index.html b/enviroments/PocketCode/index.html deleted file mode 100644 index c2a30c7..0000000 --- a/enviroments/PocketCode/index.html +++ /dev/null @@ -1,311 +0,0 @@ - - - - - - BR0TCRAFT - - - - -
-
-
Pocket Code
-
-
-
- App 1 Image -
-
- -
-
-

Pocket Code ist eine von Catroabt programmierte Entwicklungsumgebung fürs Handy. Darin lassen sich Spiele fürs Handy am Handy entwickeln. Ich selber habe dort auch schon das ein oder andere gemacht, von denen auch ein paar Apps online zu finden sind.

-
-
- - - - - - - - - - - - - diff --git a/ext/scene-manager.js b/ext/scene-manager.js new file mode 100644 index 0000000..71000ee --- /dev/null +++ b/ext/scene-manager.js @@ -0,0 +1,617 @@ +(function (Scratch) { + 'use strict'; + + if (!Scratch.extensions.unsandboxed) { + throw new Error('SceneManager must run unsandboxed!'); + } + + let rootDirectoryHandle = null; + let currentFileHandle = null; + + const styleEl = document.createElement('style'); + styleEl.textContent = ` + /* ── Overlay & Box ───────────────────────────────────────────── */ + .sm-ov { + position: fixed; inset: 0; + background: rgba(0,0,0,.6); + display: flex; align-items: center; justify-content: center; + z-index: 99999; + animation: smFade .14s ease; + } + @keyframes smFade { from { opacity:0 } to { opacity:1 } } + + .sm-box { + background: #1e1e2e; + border: 1px solid #383860; + border-radius: 14px; + padding: 26px 28px 22px; + max-width: 540px; + width: calc(100% - 40px); + color: #cdd6f4; + box-shadow: 0 28px 70px rgba(0,0,0,.55); + animation: smSlide .18s ease; + max-height: 90vh; + overflow-y: auto; + } + @keyframes smSlide { from { transform:translateY(-10px);opacity:0 } to { transform:none;opacity:1 } } + + /* ── Typography ──────────────────────────────────────────────── */ + .sm-box h2 { + margin: 0 0 14px; + font: 600 17px/1.3 system-ui, sans-serif; + color: #89b4fa; + display: flex; align-items: center; gap: 8px; + } + .sm-box p { + margin: 0 0 12px; + font: 400 13.5px/1.65 system-ui, sans-serif; + color: #bac2de; + } + .sm-box p:last-child { margin-bottom: 0; } + .sm-box code { + background: #313244; + border-radius: 4px; + padding: 1px 5px; + font-size: 12px; + font-family: monospace; + color: #a6e3a1; + } + + /* ── Note / Info block ───────────────────────────────────────── */ + .sm-note { + background: #252538; + border-left: 3px solid #89b4fa; + border-radius: 0 8px 8px 0; + padding: 10px 14px; + font: 400 13px/1.6 system-ui, sans-serif; + color: #bac2de; + margin: 12px 0; + } + .sm-note strong { color: #cdd6f4; font-weight: 600; } + + /* ── Path display ────────────────────────────────────────────── */ + .sm-path { + background: #181825; + border: 1px solid #45475a; + border-radius: 7px; + padding: 9px 13px; + font: 400 12.5px/1.5 monospace; + color: #a6e3a1; + margin: 12px 0; + word-break: break-all; + white-space: pre-wrap; + } + + /* ── Buttons ─────────────────────────────────────────────────── */ + .sm-row { + display: flex; gap: 10px; justify-content: flex-end; + margin-top: 22px; + flex-wrap: wrap; + } + .sm-btn { + padding: 8px 20px; + border-radius: 7px; + border: none; + cursor: pointer; + font: 600 13px system-ui, sans-serif; + transition: filter .12s, transform .1s; + white-space: nowrap; + } + .sm-btn:hover { filter: brightness(1.13); } + .sm-btn:active { transform: scale(.97); } + .sm-btn-primary { background: #89b4fa; color: #1e1e2e; } + .sm-btn-ghost { background: #313244; color: #cdd6f4; } + .sm-btn-warn { background: #f38ba8; color: #1e1e2e; } + .sm-btn:disabled { opacity: .4; cursor: default; filter: none; } + + /* ── Project tree ────────────────────────────────────────────── */ + .sm-tree { + background: #181825; + border: 1px solid #45475a; + border-radius: 8px; + max-height: 300px; + overflow-y: auto; + margin: 12px 0; + } + .sm-tree-empty { + padding: 28px 16px; + text-align: center; + color: #585b70; + font: italic 13px system-ui, sans-serif; + } + .sm-folder-row { + padding: 7px 14px; + background: #1e1e2e; + border-bottom: 1px solid #2a2a42; + font: 600 12.5px system-ui, sans-serif; + color: #fab387; + display: flex; align-items: center; gap: 7px; + user-select: none; + } + .sm-file-row { + padding: 8px 14px 8px 30px; + border-bottom: 1px solid #252538; + font: 400 13px system-ui, sans-serif; + color: #cdd6f4; + cursor: pointer; + display: flex; align-items: center; gap: 8px; + transition: background .1s; + } + .sm-file-row:last-child { border-bottom: none; } + .sm-file-row:hover { background: #313244; } + .sm-file-row.sm-sel { background: #3a3a5c; color: #89b4fa; } + + /* ── Toast ───────────────────────────────────────────────────── */ + .sm-toast { + position: fixed; bottom: 24px; right: 24px; + background: #313244; + border: 1px solid #45475a; + border-radius: 8px; + padding: 10px 16px; + font: 400 13px system-ui, sans-serif; + color: #cdd6f4; + z-index: 100000; + opacity: 0; + transform: translateY(8px); + transition: opacity .22s ease, transform .22s ease; + max-width: 320px; + box-shadow: 0 8px 24px rgba(0,0,0,.4); + } + .sm-toast.sm-toast-in { opacity: 1; transform: none; } + .sm-toast.sm-ok { border-left: 3px solid #a6e3a1; } + .sm-toast.sm-err { border-left: 3px solid #f38ba8; } + + /* ── Divider ─────────────────────────────────────────────────── */ + .sm-sep { + border: none; border-top: 1px solid #313244; + margin: 14px 0; + } + + /* ── Loading spinner ─────────────────────────────────────────── */ + .sm-spin { + display: inline-block; width: 14px; height: 14px; + border: 2px solid #45475a; + border-top-color: #89b4fa; + border-radius: 50%; + animation: smSpin .7s linear infinite; + } + @keyframes smSpin { to { transform: rotate(360deg); } } + `; + document.head.appendChild(styleEl); + + /** Opens a modal. `buildFn(box, close)` populates .sm-box and calls close(value) to resolve. */ + function openModal(buildFn) { + return new Promise(resolve => { + const overlay = document.createElement('div'); + overlay.className = 'sm-ov'; + const box = document.createElement('div'); + box.className = 'sm-box'; + overlay.appendChild(box); + document.body.appendChild(overlay); + + let closed = false; + const close = val => { + if (closed) return; + closed = true; + document.removeEventListener('keydown', onKey); + overlay.remove(); + resolve(val); + }; + const onKey = e => { if (e.key === 'Escape') close(null); }; + document.addEventListener('keydown', onKey); + overlay.addEventListener('click', e => { if (e.target === overlay) close(null); }); + + buildFn(box, close); + }); + } + + function showToast(msg, type = 'ok') { + const t = document.createElement('div'); + t.className = `sm-toast sm-${type}`; + t.textContent = msg; + document.body.appendChild(t); + requestAnimationFrame(() => requestAnimationFrame(() => t.classList.add('sm-toast-in'))); + setTimeout(() => { + t.classList.remove('sm-toast-in'); + t.addEventListener('transitionend', () => t.remove(), { once: true }); + }, 2800); + } + + class SceneManager { + + getInfo() { + return { + id: 'sceneManager', + name: 'Scene Manager', + color1: '#4A90E2', + color2: '#357ABD', + color3: '#285A8F', + blocks: [ + // ── Folder management ── + { + blockType: Scratch.BlockType.LABEL, + text: 'Root Folder' + }, + { + opcode: 'selectFolder', + blockType: Scratch.BlockType.BUTTON, + text: 'Set Root Folder', + func: 'selectFolder' + }, + { + opcode: 'browseProjects', + blockType: Scratch.BlockType.BUTTON, + text: 'Browse & Switch Project', + func: 'browseProjects' + }, + { + opcode: 'showHelp', + blockType: Scratch.BlockType.BUTTON, + text: 'How does this work?', + func: 'showHelp' + }, + // ── Scene blocks ── + { + blockType: Scratch.BlockType.LABEL, + text: 'Scene Blocks' + }, + { + opcode: 'openSB3', + blockType: Scratch.BlockType.COMMAND, + text: 'open [arg0].sb3', + arguments: { + arg0: { type: Scratch.ArgumentType.STRING, defaultValue: 'MyGame/Level2' } + } + }, + { + opcode: 'openSB3withData', + blockType: Scratch.BlockType.COMMAND, + text: 'open [arg0].sb3 with data [arg1]', + arguments: { + arg0: { type: Scratch.ArgumentType.STRING, defaultValue: 'MyGame/Level2' }, + arg1: { type: Scratch.ArgumentType.STRING, defaultValue: 'score=100' } + } + }, + { + opcode: 'receivedData', + blockType: Scratch.BlockType.REPORTER, + text: 'received data' + }, { + blockType: Scratch.BlockType.LABEL, + text: 'SE Support: Coming in v1.0' + } + + ] + }; + } + + async selectFolder() { + const handle = await this._pickDirectory(); + if (!handle) return; + rootDirectoryHandle = handle; + currentFileHandle = null; + this._showFolderSetModal(handle.name); + } + + async browseProjects() { + if (!rootDirectoryHandle) { + const ok = await this._requireFolder('Open the project browser'); + if (!ok) return; + } + + const files = await this._scanSb3(rootDirectoryHandle); + + await openModal((box, close) => { + let selectedPath = null; + + // Build tree HTML + const grouped = {}; + for (const f of files) { + const parts = f.path.split('/'); + const dir = parts.length > 1 ? parts.slice(0, -1).join('/') : ''; + (grouped[dir] = grouped[dir] || []).push(f); + } + + let treeHtml = ''; + if (files.length === 0) { + treeHtml = '
No .sb3 files found in this folder.
'; + } else { + for (const [dir, items] of Object.entries(grouped)) { + if (dir) treeHtml += `
📁 ${this._esc(dir)}/
`; + for (const item of items) { + const name = item.path.split('/').pop().replace(/\.sb3$/i, ''); + treeHtml += `
📄 ${this._esc(name)}.sb3
`; + } + } + } + + box.innerHTML = ` +

Browse Projects

+

Root folder: ${this._esc(rootDirectoryHandle.name)}/

+
${treeHtml}
+

+
+ + +
+ `; + + const tree = box.querySelector('#sm-tree'); + const switchBtn = box.querySelector('#sm-switch-btn'); + const selLabel = box.querySelector('#sm-sel-label'); + + tree.addEventListener('click', e => { + const row = e.target.closest('.sm-file-row'); + if (!row) return; + tree.querySelectorAll('.sm-sel').forEach(el => el.classList.remove('sm-sel')); + row.classList.add('sm-sel'); + selectedPath = row.dataset.path; + selLabel.textContent = `Selected: ${selectedPath}`; + selLabel.style.color = '#89b4fa'; + switchBtn.disabled = false; + }); + + box.querySelector('#sm-cancel-btn').onclick = () => close(null); + + switchBtn.onclick = () => { + if (!selectedPath) return; + close(selectedPath); + }; + + }).then(async path => { + if (!path) return; + await this._loadScene(path, '', /* saveFirst */ true); + }); + } + + async showHelp() { + await openModal((box, close) => { + box.innerHTML = ` +

How the Scene Manager works

+ +

+ Scene Manager lets you split your Scratch project + into multiple .sb3 files and seamlessly switch between them at runtime — + just like loading a new level or screen. +

+ +
+ Scratch Everywhere
+ All .sb3 files directly inside the scratch-everywhere folder appear in the SE! menu.

+ Set Root Folder tells Scene Manager which folder acts as that root folder while + you're developing in Scratch/Turbowarp/ScratchBox/Mistwarp or any other SE! compatible editor. +
+ +
+

Recommended project structure

+
📁 scratch-everywhere/ + MyGame.sb3 ← launcher (appears in menu) + 📁 MyGameName-Scenes/ + Level1.sb3 + Level2.sb3 + BossLevel.sb3
+ +

+ Only MyGame.sb3 is visible in the game-selection menu. + It uses Scene Manager blocks to load the actual levels from the + scenes/ subfolder, those files stay private inside the SE! menu. +

+ +
+ Use subfolder-prefixed paths in the scene blocks:
+ open scenes/Level1.sb3
+ Scene Manager navigates into the subfolder automatically. +
+ +
+ +
+ `; + box.querySelector('#sm-ok-btn').onclick = () => close(null); + }); + } + + async openSB3(args) { + await this._loadScene(args.arg0, ''); + } + + async openSB3withData(args) { + await this._loadScene(args.arg0, args.arg1); + } + + receivedData() { + return sessionStorage.getItem('scenemanager_data') || ''; + } + + /** Opens a directory picker. Returns null if cancelled. */ + async _pickDirectory() { + try { + return await window.showDirectoryPicker({ id: 'scenemanager-root', mode: 'readwrite', startIn: 'documents' }); + } catch (e) { + if (e.name !== 'AbortError') console.error('[SceneManager] Directory picker failed:', e); + return null; + } + } + + /** Shows the "folder has been set" confirmation modal. */ + _showFolderSetModal(name) { + openModal((box, close) => { + box.innerHTML = ` +

Root folder set

+

Scene Manager will now load projects from:

+
📁 ${this._esc(name)}/
+
+ In Scratch Everywhere this corresponds to the + scratch-everywhere/ folder, where all your .sb3 files are located. + Put your launcher project in the root and keep scene files in a subfolder. + You theoretically don't have to put all your scenes inside a subfolder, but it is recommended. +
+
+ +
+ `; + box.querySelector('#sm-ok-btn').onclick = () => close(null); + }); + } + + /** + * If no root folder is set, prompts the user to select one. + * Returns true if a folder is now available, false if cancelled. + */ + async _requireFolder(action) { + return openModal((box, close) => { + box.innerHTML = ` +

Root folder required

+

+ ${this._esc(action)} needs to know + where your .sb3 files are stored. +

+
+ Select your scratch-everywhere/ folder (or any folder containing your projects). + After selecting, the action will continue automatically. +
+
+ + +
+ `; + box.querySelector('#sm-cancel-btn').onclick = () => close(false); + box.querySelector('#sm-select-btn').onclick = async () => { + const handle = await this._pickDirectory(); + if (handle) { rootDirectoryHandle = handle; currentFileHandle = null; close(true); } + else close(false); + }; + }); + } + + /** Recursively scans a directory for .sb3 files. Returns [{path, handle}]. */ + async _scanSb3(dirHandle, prefix = '') { + const results = []; + for await (const [name, handle] of dirHandle.entries()) { + const rel = prefix ? `${prefix}/${name}` : name; + if (handle.kind === 'file' && name.toLowerCase().endsWith('.sb3')) { + results.push({ path: rel, handle }); + } else if (handle.kind === 'directory') { + results.push(...await this._scanSb3(handle, rel)); + } + } + return results.sort((a, b) => a.path.localeCompare(b.path)); + } + + /** + * Resolves a relative path (e.g. "scenes/Level2" or "scenes/Level2.sb3") + * to a FileSystemFileHandle inside rootDirectoryHandle. + */ + async _getFileHandle(relativePath) { + if (!rootDirectoryHandle) { + const ok = await this._requireFolder('A scene block tried to load a file'); + if (!ok) return null; + } + // Normalise: strip trailing .sb3, then re-add + const clean = relativePath.replace(/\.sb3$/i, ''); + const parts = clean.split('/').filter(Boolean); + const fileName = parts[parts.length - 1] + '.sb3'; + let handle = rootDirectoryHandle; + try { + for (let i = 0; i < parts.length - 1; i++) { + handle = await handle.getDirectoryHandle(parts[i]); + } + return await handle.getFileHandle(fileName); + } catch { + await this._showError(`File not found: ${clean}.sb3`); + return null; + } + } + + /** Core loading routine used by both the scene blocks and the project browser. */ + async _loadScene(filePath, dataToPass, fromBrowser = false) { + const display = filePath.replace(/\.sb3$/i, '') + '.sb3'; + + // Confirm + const confirmed = await openModal((box, close) => { + const saveRow = currentFileHandle + ? `` + : ``; + + box.innerHTML = ` +

Switch scene

+

Load ${this._esc(display)}?

+
+ Unsaved changes in the current project will be lost. + ${currentFileHandle ? 'You can save first using the button below.' : ''} +
+
+ + ${saveRow} + ${currentFileHandle ? `` : ''} +
+ `; + + box.querySelector('#sm-cancel-btn').onclick = () => close('cancel'); + + const switchBtn = box.querySelector('#sm-switch-btn'); + if (switchBtn) switchBtn.onclick = () => close('switch'); + + const saveBtn = box.querySelector('#sm-save-btn'); + if (saveBtn) saveBtn.onclick = () => close('save-and-switch'); + }); + + if (!confirmed || confirmed === 'cancel') return; + + // Optionally save current project back to its file + if (confirmed === 'save-and-switch' && currentFileHandle) { + try { + const blob = await Scratch.vm.saveProjectSb3(); + const writable = await currentFileHandle.createWritable(); + await writable.write(blob); + await writable.close(); + showToast(`Saved: ${currentFileHandle.name}`, 'ok'); + } catch (e) { + console.error('[SceneManager] Save failed:', e); + showToast('Save failed — switching anyway.', 'err'); + } + } + + // Resolve file + const fh = await this._getFileHandle(filePath); + if (!fh) return; + + // Load + try { + const file = await fh.getFile(); + const buf = await file.arrayBuffer(); + sessionStorage.setItem('scenemanager_data', dataToPass); + currentFileHandle = fh; + await Scratch.vm.loadProject(buf); + Scratch.vm.greenFlag(); + } catch (e) { + console.error('[SceneManager] Load failed:', e); + await this._showError('Could not load the project. Is it a valid .sb3 file?'); + } + } + + async _showError(msg) { + await openModal((box, close) => { + box.innerHTML = ` +

Error

+

${this._esc(msg)}

+
+ +
+ `; + box.querySelector('#sm-ok-btn').onclick = () => close(null); + }); + } + + _esc(str) { + return String(str) + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"'); + } + } + + Scratch.extensions.register(new SceneManager()); + +})(Scratch); \ No newline at end of file diff --git a/extensions.html b/extensions.html new file mode 100644 index 0000000..8bbeac0 --- /dev/null +++ b/extensions.html @@ -0,0 +1,388 @@ + + + + + + + BR0TCRAFT | Extensions + + + + + + + + + +
+
+
+ +

Scratch Extensions

+
+

+ Eine Sammlung von mir erstellter Erweiterungen für TurboWarp, PenguinMod und Scratch Everywhere. +

+ +
+ +
+ + + + + + +
+
+ +
+ +
+
+
+ +
Link kopiert!
+ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/fonts/LICENSE-Roboto.txt b/fonts/LICENSE-Roboto.txt deleted file mode 100644 index d645695..0000000 --- a/fonts/LICENSE-Roboto.txt +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/fonts/Orbitron-Black.ttf b/fonts/Orbitron-Black.ttf deleted file mode 100644 index cea4768..0000000 Binary files a/fonts/Orbitron-Black.ttf and /dev/null differ diff --git a/fonts/Roboto-Regular.ttf b/fonts/Roboto-Regular.ttf deleted file mode 100644 index ddf4bfa..0000000 Binary files a/fonts/Roboto-Regular.ttf and /dev/null differ diff --git a/img/Apps/PocketCode/Sensor/Sensor.png b/img/Apps/PocketCode/Sensor/Sensor.png deleted file mode 100644 index fb08ea7..0000000 Binary files a/img/Apps/PocketCode/Sensor/Sensor.png and /dev/null differ diff --git a/img/Apps/PocketCode/Tower/Tower.png b/img/Apps/PocketCode/Tower/Tower.png deleted file mode 100644 index a765182..0000000 Binary files a/img/Apps/PocketCode/Tower/Tower.png and /dev/null differ diff --git a/img/Apps/PocketCode/Tower/Tower1.jpg b/img/Apps/PocketCode/Tower/Tower1.jpg deleted file mode 100644 index bb11dbf..0000000 Binary files a/img/Apps/PocketCode/Tower/Tower1.jpg and /dev/null differ diff --git a/img/Apps/PocketCode/Tower/Tower2.jpg b/img/Apps/PocketCode/Tower/Tower2.jpg deleted file mode 100644 index 075d76c..0000000 Binary files a/img/Apps/PocketCode/Tower/Tower2.jpg and /dev/null differ diff --git a/img/Apps/PocketCode/auto-clicker/auto-clicker.png b/img/Apps/PocketCode/auto-clicker/auto-clicker.png deleted file mode 100644 index 055ff23..0000000 Binary files a/img/Apps/PocketCode/auto-clicker/auto-clicker.png and /dev/null differ diff --git a/img/Apps/PocketCode/auto-clicker/clicker1.jpg b/img/Apps/PocketCode/auto-clicker/clicker1.jpg deleted file mode 100644 index c2ca014..0000000 Binary files a/img/Apps/PocketCode/auto-clicker/clicker1.jpg and /dev/null differ diff --git a/img/Apps/PocketCode/auto-clicker/clicker2.jpg b/img/Apps/PocketCode/auto-clicker/clicker2.jpg deleted file mode 100644 index 265f118..0000000 Binary files a/img/Apps/PocketCode/auto-clicker/clicker2.jpg and /dev/null differ diff --git a/img/Apps/Zynk/Banner.png b/img/Apps/Zynk/Banner.png new file mode 100644 index 0000000..5a564d0 Binary files /dev/null and b/img/Apps/Zynk/Banner.png differ diff --git a/img/Apps/Zynk/Logo.png b/img/Apps/Zynk/Logo.png new file mode 100644 index 0000000..c67c5d2 Binary files /dev/null and b/img/Apps/Zynk/Logo.png differ diff --git a/img/Apps/Zynk/Logo.svg b/img/Apps/Zynk/Logo.svg new file mode 100644 index 0000000..63bd230 --- /dev/null +++ b/img/Apps/Zynk/Logo.svg @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + View 1 + + + + + + + + + + + + + + + + + + + + + + + + + YNK + \ No newline at end of file diff --git a/img/Apps/Zynk/Logo2.svg b/img/Apps/Zynk/Logo2.svg new file mode 100644 index 0000000..ea8bc16 --- /dev/null +++ b/img/Apps/Zynk/Logo2.svg @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + View 1 + + + + + + + + + + + + + Bildschirmfoto vom 2026-02-03 20-05-26 + + + + + + + + + + + + + + + YNK + Control your device with ease + \ No newline at end of file diff --git a/img/Background2.jpg b/img/Background2.jpg deleted file mode 100644 index 1f8eaee..0000000 Binary files a/img/Background2.jpg and /dev/null differ diff --git a/img/Background3.jpg b/img/Background3.jpg deleted file mode 100644 index e6d4ec1..0000000 Binary files a/img/Background3.jpg and /dev/null differ diff --git a/img/Logo.png b/img/Logo.png deleted file mode 100644 index 06b2224..0000000 Binary files a/img/Logo.png and /dev/null differ diff --git a/img/avatar.png b/img/avatar.png new file mode 100644 index 0000000..a070c87 Binary files /dev/null and b/img/avatar.png differ diff --git a/img/contact/discord.png b/img/contact/discord.png deleted file mode 100644 index 239a6e7..0000000 Binary files a/img/contact/discord.png and /dev/null differ diff --git a/img/contact/email.png b/img/contact/email.png deleted file mode 100644 index 5df053b..0000000 Binary files a/img/contact/email.png and /dev/null differ diff --git a/img/contact/github.png b/img/contact/github.png deleted file mode 100644 index 50b8175..0000000 Binary files a/img/contact/github.png and /dev/null differ diff --git a/img/developmentEnvironment/PocketCode.jpeg b/img/developmentEnvironment/PocketCode.jpeg deleted file mode 100644 index 82cb8ea..0000000 Binary files a/img/developmentEnvironment/PocketCode.jpeg and /dev/null differ diff --git a/img/developmentEnvironment/PocketCodetiny.png b/img/developmentEnvironment/PocketCodetiny.png deleted file mode 100644 index 8cd033c..0000000 Binary files a/img/developmentEnvironment/PocketCodetiny.png and /dev/null differ diff --git a/img/errors/404.jpg b/img/errors/404.jpg deleted file mode 100644 index 35875c6..0000000 Binary files a/img/errors/404.jpg and /dev/null differ diff --git a/img/extensions/scene-manager.png b/img/extensions/scene-manager.png new file mode 100644 index 0000000..6252f84 Binary files /dev/null and b/img/extensions/scene-manager.png differ diff --git a/index.html b/index.html index ee87ec5..73813dd 100644 --- a/index.html +++ b/index.html @@ -1,99 +1,266 @@ - + + - - - BR0TCRAFT - - -
-

BR0TCRAFT

-
-
- Header Image -
-
- Discord - Email - Email -
- - + + .hero-short strong { + color: #fff; + font-weight: 600; + } + + .expand-toggle { + background: none; + border: none; + color: var(--accent-primary); + font-family: var(--font-code); + font-size: 0.85rem; + cursor: pointer; + padding: 0; + margin-top: 0.75rem; + display: inline-flex; + align-items: center; + gap: 0.4rem; + transition: opacity 0.2s; + } + + .expand-toggle:hover { + opacity: 0.7; + } + + .hero-long { + display: none; + font-size: 1rem; + color: var(--text-muted); + line-height: 1.75; + max-width: 600px; + margin-top: 1rem; + animation: fadeIn 0.3s ease; + } + + .hero-long.visible { + display: block; + } + + @keyframes fadeIn { + from { + opacity: 0; + transform: translateY(4px); + } + + to { + opacity: 1; + transform: translateY(0); + } + } + + /* Current section */ + .current-block { + border-left: 2px solid var(--accent-primary); + padding: 1.5rem 2rem; + background: rgba(255, 77, 77, 0.04); + border-radius: 0 var(--radius) var(--radius) 0; + } + + .current-label { + font-family: var(--font-code); + font-size: 0.75rem; + color: var(--accent-primary); + letter-spacing: 0.1em; + text-transform: uppercase; + margin-bottom: 0.75rem; + } + + .current-block h3 { + font-family: var(--font-display); + font-size: 1.1rem; + color: #fff; + margin-bottom: 0.5rem; + } + + .current-block p { + color: var(--text-muted); + font-size: 0.95rem; + line-height: 1.65; + } + + .current-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); + gap: 1rem; + margin-top: 1.5rem; + } + + /* Pinned article */ + .pinned-card { + border: 1px solid #2a2a2a; + border-radius: var(--radius); + padding: 1.5rem; + background: rgba(255, 255, 255, 0.02); + transition: border-color 0.2s; + } + + .pinned-card:hover { + border-color: var(--accent-primary); + } + + .pinned-tag { + font-family: var(--font-code); + font-size: 0.75rem; + color: var(--accent-primary); + margin-bottom: 0.6rem; + display: block; + } + + .pinned-card h3 { + color: #fff; + font-size: 1rem; + margin-bottom: 0.4rem; + } + + .pinned-card p { + color: var(--text-muted); + font-size: 0.9rem; + line-height: 1.6; + margin-bottom: 1rem; + } + + + + + + + +
+ + +
+
+ +
+ + +

Br0tcraft

+ +

+ Ich baue Software, um zu verstehen, wie sie im Kern funktioniert. Am liebsten mit C++, Python oder Dart. Ob + technische Spielerei oder Nischen-Tools, ich mag Projekte, die nicht nur laufen, sondern auch eine saubere + Struktur unter der Haube haben. +
+ Work in progress, mit Liebe zum Detail. +

+ +
+ C++ + C# + GDScript + Python + JavaScript + Dart + Scratch +
+ + +
+ +
+ Br0tcraft Avatar +
+ +
+
+ +
+

+ Contributions & Open Source +

+

+ Ich bin definitiv kein Profi. Oft sitze ich vor dem Code und raufe mir die Haare, aber genau das macht den + Reiz aus. Sich an Projekte wagen, die eigentlich eine Nummer zu groß sind. +

+ +
+ +
+
+ +
+

Scratch Everywhere

+ +
+
+
+

+ Es ist ein Projekt, das Scratch auf Plattformen wie die Wii, den 3DS oder Linux + bringt. + Ich lerne hier mehr über C++ und Teamwork als in jedem Tutorial ... auch wenn meine Pull Requests manchmal + zehn Anläufe brauchen, bis sie passen. +

+
    +
  • Arbeit am C++ Kern und der UI-Logik
  • +
  • Fixing von Bugs (die ich oft erst selbst eingebaut habe)
  • +
  • Alte Hardware mit modernem Code zum Leben erwecken
  • +
+
+ Aktiv dabei +
+
+
+ +
+
+ +
+ + + + \ No newline at end of file diff --git a/mini/assets/icons/arrow_down.svg b/mini/assets/icons/arrow_down.svg new file mode 100644 index 0000000..15fe828 --- /dev/null +++ b/mini/assets/icons/arrow_down.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/mini/assets/icons/arrow_left.svg b/mini/assets/icons/arrow_left.svg new file mode 100644 index 0000000..4f51a8e --- /dev/null +++ b/mini/assets/icons/arrow_left.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/mini/assets/icons/arrow_right.svg b/mini/assets/icons/arrow_right.svg new file mode 100644 index 0000000..fca2885 --- /dev/null +++ b/mini/assets/icons/arrow_right.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/mini/assets/icons/book.svg b/mini/assets/icons/book.svg new file mode 100644 index 0000000..c0581b7 --- /dev/null +++ b/mini/assets/icons/book.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/mini/assets/icons/church.svg b/mini/assets/icons/church.svg new file mode 100644 index 0000000..9b74bb7 --- /dev/null +++ b/mini/assets/icons/church.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/mini/assets/icons/download.svg b/mini/assets/icons/download.svg new file mode 100644 index 0000000..4e620cf --- /dev/null +++ b/mini/assets/icons/download.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/mini/assets/icons/file.svg b/mini/assets/icons/file.svg new file mode 100644 index 0000000..46b3ce4 --- /dev/null +++ b/mini/assets/icons/file.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/mini/assets/icons/list.svg b/mini/assets/icons/list.svg new file mode 100644 index 0000000..ac70ef1 --- /dev/null +++ b/mini/assets/icons/list.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/mini/assets/icons/settings.svg b/mini/assets/icons/settings.svg new file mode 100644 index 0000000..8c2f52f --- /dev/null +++ b/mini/assets/icons/settings.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/mini/assets/icons/shirt.svg b/mini/assets/icons/shirt.svg new file mode 100644 index 0000000..ccb99a6 --- /dev/null +++ b/mini/assets/icons/shirt.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/mini/assets/icons/sun.svg b/mini/assets/icons/sun.svg new file mode 100644 index 0000000..993a35c --- /dev/null +++ b/mini/assets/icons/sun.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/mini/assets/icons/users.svg b/mini/assets/icons/users.svg new file mode 100644 index 0000000..cc949b0 --- /dev/null +++ b/mini/assets/icons/users.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/mini/assets/icons/x.svg b/mini/assets/icons/x.svg new file mode 100644 index 0000000..c84a55c --- /dev/null +++ b/mini/assets/icons/x.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/mini/assets/images/placement/background.svg b/mini/assets/images/placement/background.svg new file mode 100644 index 0000000..3d0284f --- /dev/null +++ b/mini/assets/images/placement/background.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/mini/assets/images/placement/entrance.svg b/mini/assets/images/placement/entrance.svg new file mode 100644 index 0000000..e86b285 --- /dev/null +++ b/mini/assets/images/placement/entrance.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/mini/assets/images/placement/entrance_incense.svg b/mini/assets/images/placement/entrance_incense.svg new file mode 100644 index 0000000..1254a18 --- /dev/null +++ b/mini/assets/images/placement/entrance_incense.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/mini/assets/images/placement/kneel.svg b/mini/assets/images/placement/kneel.svg new file mode 100644 index 0000000..d97b922 --- /dev/null +++ b/mini/assets/images/placement/kneel.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/mini/assets/images/placement/kneel_incense.svg b/mini/assets/images/placement/kneel_incense.svg new file mode 100644 index 0000000..b2d8fa2 --- /dev/null +++ b/mini/assets/images/placement/kneel_incense.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/mini/assets/images/placement/sit.svg b/mini/assets/images/placement/sit.svg new file mode 100644 index 0000000..792f5e7 --- /dev/null +++ b/mini/assets/images/placement/sit.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/mini/assets/images/placement/sit_incense.svg b/mini/assets/images/placement/sit_incense.svg new file mode 100644 index 0000000..5561be6 --- /dev/null +++ b/mini/assets/images/placement/sit_incense.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/mini/assets/images/tasks/basket.svg b/mini/assets/images/tasks/basket.svg new file mode 100644 index 0000000..4199fb8 --- /dev/null +++ b/mini/assets/images/tasks/basket.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/mini/assets/images/tasks/bread-wine.svg b/mini/assets/images/tasks/bread-wine.svg new file mode 100644 index 0000000..9dc0068 --- /dev/null +++ b/mini/assets/images/tasks/bread-wine.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/mini/assets/images/tasks/candle.svg b/mini/assets/images/tasks/candle.svg new file mode 100644 index 0000000..6bf9d34 --- /dev/null +++ b/mini/assets/images/tasks/candle.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/mini/assets/images/tasks/incense.svg b/mini/assets/images/tasks/incense.svg new file mode 100644 index 0000000..50e7a3b --- /dev/null +++ b/mini/assets/images/tasks/incense.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/mini/data/knowledge/Ambo.html b/mini/data/knowledge/Ambo.html new file mode 100644 index 0000000..868b525 --- /dev/null +++ b/mini/data/knowledge/Ambo.html @@ -0,0 +1,11 @@ +

+ Der Ambo ist das feststehende, leicht + erhöhte Lesepult in der Kirche. +

+

+ An dem Ambo findet das Lektorat statt, dies sind die Lesungen aus der Bibel und die + Fürbitten. +

+

+ In unserer Gemeinde ist der Ambo (vom Pfarrer aus gesehen) rechts neben dem Altar platziert. +

\ No newline at end of file diff --git a/mini/data/knowledge/Kollekte.html b/mini/data/knowledge/Kollekte.html new file mode 100644 index 0000000..5f31590 --- /dev/null +++ b/mini/data/knowledge/Kollekte.html @@ -0,0 +1,4 @@ +

Die Kollekte ist eine + Geldsammlung während oder am Ende eines Gottesdienstes.

+

Sie dient dazu, Geld für kirchliche, soziale oder wohltätige Zwecke zu sammeln.

+

Der Begriff kommt aus dem Lateinischen und bedeutet "sammeln".

\ No newline at end of file diff --git a/mini/data/knowledge/Liturgische_Farben.html b/mini/data/knowledge/Liturgische_Farben.html new file mode 100644 index 0000000..5ad7638 --- /dev/null +++ b/mini/data/knowledge/Liturgische_Farben.html @@ -0,0 +1,7 @@ +

Die Farben der Gewänder folgen dem Kirchenjahr:

+
    +
  • Grün – Zeit im Jahreskreis
  • +
  • Lila – Fastenzeit und Advent
  • +
  • Rot – Feste des Heiligen Geistes und der Märtyrer
  • +
  • Weiß/Gold – Hochfeste wie Weihnachten und Ostern
  • +
\ No newline at end of file diff --git a/mini/data/knowledge/Praefation.html b/mini/data/knowledge/Praefation.html new file mode 100644 index 0000000..63b3765 --- /dev/null +++ b/mini/data/knowledge/Praefation.html @@ -0,0 +1,13 @@ +

Der Pfarrer wendet sich zur Gemeinde:

+
+

Pfarrer: Der Herr sei mit euch.
+ Gemeinde: Und mit deinem Geist. +

+

Pfarrer: Erhebet die Herzen.
+ Gemeinde: Wir haben sie beim Herrn. +

+

Pfarrer: Lasset uns danken dem Herrn, unserm Gott.
+ Gemeinde: Das ist würdig und recht. +

+
+

„In Wahrheit ist es würdig und recht, dir, Herr, heiliger Vater, immer und überall zu danken…“

\ No newline at end of file diff --git a/mini/data/knowledge/knowledge.json b/mini/data/knowledge/knowledge.json new file mode 100644 index 0000000..e8c7f7d --- /dev/null +++ b/mini/data/knowledge/knowledge.json @@ -0,0 +1,22 @@ +[ + { + "title": "Liturgische Farben", + "category": "Tradition", + "file": "Liturgische_Farben.html" + }, + { + "title": "Ambo", + "category": "Gegenstand", + "file": "Ambo.html" + }, + { + "title": "Kollekte", + "category": "Tradition", + "file": "Kollekte.html" + }, + { + "title": "Präfation (Dialog)", + "category": "Ablauf", + "file": "Praefation.html" + } +] \ No newline at end of file diff --git a/mini/data/placements.json b/mini/data/placements.json new file mode 100644 index 0000000..c9d20f0 --- /dev/null +++ b/mini/data/placements.json @@ -0,0 +1,30 @@ +{ + "standard": [ + { + "label": "Einzug", + "image": "assets/images/placement/entrance.svg" + }, + { + "label": "Hinknien", + "image": "assets/images/placement/kneel.svg" + }, + { + "label": "Hinsetzen", + "image": "assets/images/placement/sit.svg" + } + ], + "withWeihrauch": [ + { + "label": "Einzug (mit Weihrauch)", + "image": "assets/images/placement/entrance_incense.svg" + }, + { + "label": "Hinknien (mit Weihrauch)", + "image": "assets/images/placement/kneel_incense.svg" + }, + { + "label": "Hinsetzen (mit Weihrauch)", + "image": "assets/images/placement/sit_incense.svg" + } + ] +} \ No newline at end of file diff --git a/mini/data/prayers/Vater_unser.html b/mini/data/prayers/Vater_unser.html new file mode 100644 index 0000000..3a3375d --- /dev/null +++ b/mini/data/prayers/Vater_unser.html @@ -0,0 +1,13 @@ +

Vater unser im Himmel,
+ geheiligt werde dein Name.
+ Dein Reich komme.
+ Dein Wille geschehe,
+ wie im Himmel so auf Erden.
+ Unser tägliches Brot gib uns heute.
+ Und vergib uns unsere Schuld,
+ wie auch wir vergeben unsern Schuldigern.
+ Und führe uns nicht in Versuchung,
+ sondern erlöse uns von dem Bösen.
+ Denn dein ist das Reich und die Kraft
+ und die Herrlichkeit in Ewigkeit.
+ Amen.

\ No newline at end of file diff --git a/mini/data/prayers/prayers.json b/mini/data/prayers/prayers.json new file mode 100644 index 0000000..425f10a --- /dev/null +++ b/mini/data/prayers/prayers.json @@ -0,0 +1,6 @@ +[ + { + "title": "Vater unser", + "file": "Vater_unser.html" + } +] \ No newline at end of file diff --git a/mini/data/sequence/00-Vorbereitung.html b/mini/data/sequence/00-Vorbereitung.html new file mode 100644 index 0000000..e4b080f --- /dev/null +++ b/mini/data/sequence/00-Vorbereitung.html @@ -0,0 +1,9 @@ +Bespricht kurz, wer welche Aufgabe übernimmt.

+Sollten nur 4 Minis anwesend sein, entfällt der Weihrauchdienst, die beiden Minis, die sich um die Kerze kümmern, +übernehmen dann zusätzlich die Körbe.
+Der Kollekte-Dienst ist nicht immer vorgesehen, fragt daher sicherheitshalber beim Pfarrer nach, falls ihr unsicher +seid. +

+Stellt euch nun vor dem Kreuz in der Sakristei auf (so wie ihr später auch beim Einzug gehen werdet).
+Der Pfarrer sagt: "Unsere Hilfe ist im Namen des Herrn."
+Ihr antwortet: "Der Himmel und Erde erschaffen hat." und macht dabei das Kreuzzeichen. \ No newline at end of file diff --git a/mini/data/sequence/01-Einzug.html b/mini/data/sequence/01-Einzug.html new file mode 100644 index 0000000..0cf6092 --- /dev/null +++ b/mini/data/sequence/01-Einzug.html @@ -0,0 +1,6 @@ +Die Minis, die für die Gabenbereitung zuständig sind, bleiben beim Einzug kurz neben der Glocke bei der Tür stehen.
+Stoßt die Glocke so an, dass sie etwa zweimal läutet.

+ +Danach geht ihr zu euren Knieplätzen auf der Stufe vor dem Altar.
+Sobald der Pfarrer ein Zeichen gibt (Handzeichen o. Ä.), macht ihr gemeinsam eine kurze Kniebeuge,
+geht dann hoch auf eure Sitzplätze und setzt euch möglichst synchron hin. \ No newline at end of file diff --git a/mini/data/sequence/02-Eroeffnung.html b/mini/data/sequence/02-Eroeffnung.html new file mode 100644 index 0000000..1ec6b8a --- /dev/null +++ b/mini/data/sequence/02-Eroeffnung.html @@ -0,0 +1 @@ +EMPTY \ No newline at end of file diff --git a/mini/data/sequence/sequence.json b/mini/data/sequence/sequence.json new file mode 100644 index 0000000..e6125cf --- /dev/null +++ b/mini/data/sequence/sequence.json @@ -0,0 +1,31 @@ +[ + { + "title": "Vorbereitung", + "categories": [ + "gabenbereitung", + "kerze", + "koerbchen", + "weihrauch" + ], + "file": "00-Vorbereitung.html" + }, + { + "title": "Einzug", + "categories": [ + "allgemein", + "gabenbereitung", + "kerze", + "koerbchen", + "weihrauch" + ], + "file": "01-Einzug.html" + }, + { + "title": "Eröffnung", + "categories": [ + "kerze", + "weihrauch" + ], + "file": "02-Eroeffnung.html" + } +] \ No newline at end of file diff --git a/mini/data/tasks.json b/mini/data/tasks.json new file mode 100644 index 0000000..f4a0e98 --- /dev/null +++ b/mini/data/tasks.json @@ -0,0 +1,72 @@ +{ + "gabenbereitung": { + "icon": "assets/images/tasks/bread-wine.svg", + "title": "Gabenbereitung", + "description": "Vorbereitung der Gaben für die Eucharistie und Handwaschung des Priesters", + "steps": [ + { + "title": "Vorbereitung", + "steps": [ + "Sobald der Pfarrer euch ein Zeichen gibt, geht ihr gemeinsam zum Gabentisch.", + "Ein Mini nimmt Kelch und Schale, der andere übernimmt Wasser und Wein.", + "Achtet darauf, dass die Henkel der Gläser zum Pfarrer zeigen, so kann er sie leichter greifen.", + "Macht ein leichtes Kopfnicken, dreht euch zueinander und geht zurück zum Gabentisch.", + "Dort nimmt einer die Karaffe mit Wasser und das Auffangschälchen, der andere das Handtuch.", + "Ihr geht wieder zum Pfarrer und wascht ihm die Hände (in der Regel genügen ein paar Tropfen Wasser).", + "Legt Handtuch und Karaffe zurück auf den Tisch, ihr könnt nun mit den anderen Ministranten zur Stufe gehen und euch dort gemeinsam hinknien." + ] + }, + { + "title": "Klingeln", + "steps": [ + "Nach der Wandlung läutet ihr mit der Schelle.", + "Einmal, wenn der Pfarrer die Hostie hochhebt, und einmal, wenn er den Kelch hochhebt.", + "Zuerst läutet der Mini, der rechts neben dem Altar kniet, dann der links neben dem Altar.", + "Jedes Läuten sollte etwa 2–3 Sekunden dauern." + ] + }, + { + "title": "Abtragen", + "steps": [ + "Nach dem Kommunionganges geht ihr zurück zum Gabentisch.", + "Nehmt das kleine Wasserschälchen und geht damit zum Pfarrer.", + "Er reicht euch den Kelch, und ihr könnt das restliche Wasser darin ausleeren.", + "Der Pfarrer trinkt das Wasser, reinigt Kelch und Schale mit einem Tuch und gibt sie euch zurück.", + "Stellt die Gefäße wieder auf den Gabentisch und kehrt auf eure Plätze zurück." + ] + } + ] + }, + "kerze": { + "icon": "assets/images/tasks/candle.svg", + "title": "Kerze", + "description": "Die Kerze für das Evangelium tragen", + "steps": [ + "Hinter euren Sitzplätzen stehen zwei kleinere Kerzen, nicht zu verwechseln mit den großen Kerzen weiter hinten.", + "Normalerweise sind sie bereits entzündet. Falls nicht, gebt der Mesnerin kurz ein Zeichen.", + "Nehmt die Kerzen und stellt euch hinter den Pfarrer. Folgt ihm mit etwa einem Schritt Abstand.", + "Geht vorsichtig mit den Kerzen um, heißes Wachs kann tropfen (Achtet auf die Kleidung!).", + "Während der Pfarrer am Ambo das Evangelium liest, stellt ihr euch gut sichtbar hinter ihn.", + "Macht das Kreuzzeichen mit, sobald der Pfarrer es auf seiner Stirn macht (dazu müsst ihr die Kerze kurz mit einer Hand halten).", + "Danach bleibt ihr still stehen, bis der Pfarrer fertig ist oder euch ein Zeichen zum Gehen gibt.", + "Dann geht ihr gemeinsam zurück und stellt die Kerzen ab.", + "WICHTIG: Falls es keinen separaten Kollekte-Dienst gibt, übernehmt ihr zusätzlich während der Gabenbereitung das Sammeln der Spenden mit den Körben." + ] + }, + "kollekte": { + "icon": "assets/images/tasks/basket.svg", + "title": "Kollekte", + "description": "Die Körbe für die Kollekte austeilen und wieder einsammeln", + "steps": [ + "Sobald die Gabenbereitung beginnt, nehmt ihr die Körbe. Sie liegen entweder hinter euch oder auf der anderen Seite bei den anderen Mini-Plätzen.", + "Gemeinsam geht ihr nach vorne zum Altar, macht dort eine Kniebeuge und dreht euch dann zu den Bänken um.", + "Der Mini mit den drei Körben schaut am besten schon vorher, ob oben in der Kirche auch Leute sitzen.", + "Danach gebt ihr links und rechts jeweils ein Korb her, einmal zu Beginn und einmal nach dem ersten Durchgang.", + "Die Person mit dem zusätzlichen Korb geht dann noch nach oben (falls dort jemand steht. Besser einmal zu viel hochgehen, als jemanden zu vergessen).", + "Die andere Person wartet hinten in der Kirche, bis sie die Körbe von oben zurückbekommt.", + "Anschließend geht ihr nach vorne und sammelt die gefüllten Körbe ein.", + "Legt sie dann hinter der Altarstufe auf den Boden und kniet euch auf eure Plätze.", + "Dort wartet ihr, bis die anderen Minis mit der Gabenbereitung fertig sind und ebenfalls zu euch nach unten kommen." + ] + } +} \ No newline at end of file diff --git a/mini/index.html b/mini/index.html new file mode 100644 index 0000000..6d027c0 --- /dev/null +++ b/mini/index.html @@ -0,0 +1,100 @@ + + + + + + + Ministranten Guide + + + + + +
+
+
+ Ministranten Guide Icon +
+
+
Ministranten-Guide
+
+
+
+ + + + +
+ +
+
+ + + + + + + + + + \ No newline at end of file diff --git a/mini/js/main.js b/mini/js/main.js new file mode 100644 index 0000000..71cf3e8 --- /dev/null +++ b/mini/js/main.js @@ -0,0 +1,102 @@ +import { initTodayPage } from './modules/today.js'; +import { initSequencePage } from './modules/sequence.js'; +import { initKnowledgePage } from './modules/knowledge.js'; + +document.addEventListener("DOMContentLoaded", () => { + const container = document.getElementById("content-container"); + const triggers = document.querySelectorAll("[data-file]"); + const settingsBtn = document.getElementById('settingsBtn'); + const settingsModal = document.getElementById('settingsModal'); + const closeSettingsBtn = document.getElementById('closeSettingsBtn'); + const darkModeToggle = document.getElementById('darkModeToggle'); + + settingsBtn.addEventListener('click', () => { + settingsModal.classList.add('active'); + }); + + closeSettingsBtn.addEventListener('click', () => { + settingsModal.classList.remove('active'); + }); + + settingsModal.addEventListener('click', (e) => { + if (e.target === settingsModal) { + settingsModal.classList.remove('active'); + } + }); + + document.addEventListener('keydown', (e) => { + if (e.key === 'Escape' && settingsModal.classList.contains('active')) { + settingsModal.classList.remove('active'); + } + }); + + const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)'); + const systemPrefersDark = mediaQuery.matches; + + const storedDarkMode = localStorage.getItem('darkMode'); + + let isDark; + if (storedDarkMode !== null) { + isDark = storedDarkMode === 'true'; + } else { + isDark = systemPrefersDark; + } + + function applyDarkMode(enable) { + document.body.classList.toggle('dark-mode', enable); + if (darkModeToggle) { + darkModeToggle.checked = enable; + } + } + + applyDarkMode(isDark); + + darkModeToggle.addEventListener('change', () => { + const isDarkNow = darkModeToggle.checked; + applyDarkMode(isDarkNow); + localStorage.setItem('darkMode', isDarkNow); + }); + + function handleSystemChange(e) { + if (localStorage.getItem('darkMode') === null) { + applyDarkMode(e.matches); + } + } + + if (mediaQuery.addEventListener) { + mediaQuery.addEventListener('change', handleSystemChange); + } else { + mediaQuery.addListener(handleSystemChange); + } + + + async function loadPage(file) { + try { + const response = await fetch(file); + if (!response.ok) throw new Error("Error loading page: " + response.statusText); + + container.innerHTML = await response.text(); + + triggers.forEach(item => { + item.classList.toggle("active", item.getAttribute("data-file") === file); + }); + + if (file.includes("today")) initTodayPage(); + if (file.includes("sequence")) initSequencePage(); + if (file.includes("knowledge")) initKnowledgePage(); + + } catch (error) { + container.innerHTML = "

Error loading content.

"; + console.error(error); + } + } + + loadPage("pages/today.html"); + + triggers.forEach(trigger => { + trigger.addEventListener("click", (e) => { + e.preventDefault(); + loadPage(trigger.getAttribute("data-file")); + }); + }); +}); \ No newline at end of file diff --git a/mini/js/modules/knowledge.js b/mini/js/modules/knowledge.js new file mode 100644 index 0000000..41ecb98 --- /dev/null +++ b/mini/js/modules/knowledge.js @@ -0,0 +1,95 @@ +export async function initKnowledgePage() { + const wissenList = document.getElementById('wissen-list'); + const gebeteList = document.getElementById('gebete-list'); + + if (wissenList) { + try { + const knowledgeResponse = await fetch('data/knowledge/knowledge.json'); + const knowledgeItems = await knowledgeResponse.json(); + renderKnowledgeList(wissenList, knowledgeItems); + } catch (error) { + console.error('Fehler beim Laden der Wissens-Übersicht:', error); + } + } + + if (gebeteList) { + try { + const gebeteResponse = await fetch('data/prayers/prayers.json'); + const gebeteItems = await gebeteResponse.json(); + renderGebeteList(gebeteList, gebeteItems); + } catch (error) { + console.error('Fehler beim Laden der Gebete:', error); + } + } + + const tabs = document.querySelectorAll('.wissen-tab'); + const panelGebete = document.getElementById('panel-gebete'); + const panelWissen = document.getElementById('panel-wissen'); + + tabs.forEach(tab => { + tab.addEventListener('click', () => { + tabs.forEach(t => t.classList.remove('active')); + tab.classList.add('active'); + + const target = tab.dataset.tab; + if (panelGebete) panelGebete.classList.toggle('active', target === 'gebete'); + if (panelWissen) panelWissen.classList.toggle('active', target === 'wissen'); + }); + }); +} + +function createAccordionItem(title, filename, basePath) { + const element = document.createElement('div'); + element.className = 'wissen-item'; + + element.innerHTML = ` + +
+

Loading...

+
+ `; + + const header = element.querySelector('.wissen-header'); + const contentDiv = element.querySelector('.wissen-content'); + let isLoaded = false; + + header.addEventListener('click', async () => { + element.classList.toggle('active'); + + if (element.classList.contains('active') && !isLoaded) { + try { + const contentResponse = await fetch(`${basePath}/${filename}`); + if (!contentResponse.ok) throw new Error('Failed to load file'); + + const htmlContent = await contentResponse.text(); + contentDiv.innerHTML = htmlContent; + isLoaded = true; + } catch (err) { + contentDiv.innerHTML = '

Failed to load content.

'; + } + } + }); + + return element; +} + +function renderKnowledgeList(container, items) { + container.innerHTML = ''; + items.forEach(item => { + const title = item.titel || item.title; + const element = createAccordionItem(title, item.file, 'data/knowledge'); + container.appendChild(element); + }); +} + +function renderGebeteList(container, items) { + container.innerHTML = ''; + items.forEach(item => { + const title = item.title || item.titel; + const element = createAccordionItem(title, item.file, 'data/prayers'); + container.appendChild(element); + }); +} \ No newline at end of file diff --git a/mini/js/modules/sequence.js b/mini/js/modules/sequence.js new file mode 100644 index 0000000..33e28f8 --- /dev/null +++ b/mini/js/modules/sequence.js @@ -0,0 +1,120 @@ +export async function initSequencePage() { + + const listContainer = document.getElementById('sequence-list') || document.getElementById('ablauf-list'); + const filterChips = document.querySelectorAll('.filter-chip'); + + if (!listContainer) { + console.warn('Sequence container not found.'); + return; + } + + try { + const response = await fetch('data/sequence/sequence.json'); + if (!response.ok) throw new Error('Sequence data could not be loaded.'); + + const sequenceSteps = await response.json(); + + renderSequenceList(listContainer, sequenceSteps); + setupFilterLogic(filterChips); + + } catch (error) { + console.error('Error loading sequence:', error); + listContainer.innerHTML = '

Sequence could not be loaded.

'; + } +} + +function renderSequenceList(container, steps) { + container.innerHTML = ''; + + steps.forEach(step => { + const item = document.createElement('div'); + item.className = 'ablauf-item'; + item.dataset.categories = step.categories.join(','); + + item.innerHTML = ` + +
+

Loading...

+
+ `; + + const header = item.querySelector('.sequence-header'); + const contentDiv = item.querySelector('.sequence-content'); + let isLoaded = false; + + header.addEventListener('click', async () => { + item.classList.toggle('active'); + + if (item.classList.contains('active') && !isLoaded) { + try { + const contentResponse = await fetch(`data/sequence/${step.file}`); + if (!contentResponse.ok) throw new Error('Content not found.'); + + const htmlContent = await contentResponse.text(); + contentDiv.innerHTML = htmlContent; + isLoaded = true; + } catch (err) { + console.error(err); + contentDiv.innerHTML = '

Content could not be loaded.

'; + } + } + }); + + container.appendChild(item); + }); + +} + +function setupFilterLogic(filterChips) { + if (!filterChips || filterChips.length === 0) return; + + let activeFilters = new Set(['alle']); + + function applyFilters() { + document.querySelectorAll('.ablauf-item').forEach(item => { + const categories = item.dataset.categories ? item.dataset.categories.split(',') : []; + + const isVisible = activeFilters.has('alle') || + categories.some(cat => activeFilters.has(cat)); + + item.style.display = isVisible ? '' : 'none'; + }); + } + + filterChips.forEach(chip => { + chip.addEventListener('click', () => { + const filter = chip.dataset.filter; + + if (filter === 'alle') { + activeFilters = new Set(['alle']); + filterChips.forEach(c => c.classList.remove('active')); + chip.classList.add('active'); + } else { + if (activeFilters.has('alle')) { + activeFilters.delete('alle'); + document.querySelector('.filter-chip[data-filter="alle"]')?.classList.remove('active'); + } + + if (activeFilters.has(filter)) { + activeFilters.delete(filter); + chip.classList.remove('active'); + } else { + activeFilters.add(filter); + chip.classList.add('active'); + } + + if (activeFilters.size === 0) { + activeFilters.add('alle'); + document.querySelector('.filter-chip[data-filter="alle"]')?.classList.add('active'); + } + } + + applyFilters(); + }); + }); + + applyFilters(); +} \ No newline at end of file diff --git a/mini/js/modules/today.js b/mini/js/modules/today.js new file mode 100644 index 0000000..6815d94 --- /dev/null +++ b/mini/js/modules/today.js @@ -0,0 +1,227 @@ +export async function initTodayPage() { + + const today = new Date(); + document.getElementById('weekday').textContent = today.toLocaleDateString('de-DE', { weekday: 'long' }); + document.getElementById('date').textContent = today.toLocaleDateString('de-DE', { day: '2-digit', month: 'long', year: 'numeric' }); + + const miniColor = getMiniColor(today); + const miniAmount = getMiniAmount(today); + + document.getElementById('summary-color').textContent = miniColor; + document.getElementById('summary-count').textContent = miniAmount; + + const modal = document.getElementById('task-modal'); + if (!modal) return; + + try { + const [tasksResponse, placementsResponse] = await Promise.all([ + fetch('data/tasks.json'), + fetch('data/placements.json') + ]); + + const taskData = await tasksResponse.json(); + const placementData = await placementsResponse.json(); + + setupTaskModal(modal, taskData); + setupPlacementViewer(placementData); + } catch (error) { + console.error('Error while loading data:', error); + } +} + +function setupTaskModal(modal, taskData) { + const modalIcon = document.getElementById('modal-icon'); + const modalTitle = document.getElementById('modal-title'); + const modalDescription = document.getElementById('modal-description'); + const modalSteps = document.getElementById('modal-steps'); + const closeBtn = modal.querySelector('.modal-close'); + const segContainer = document.getElementById('task-segmented-control'); + + document.querySelectorAll('.task-card').forEach(card => { + card.addEventListener('click', () => { + const data = taskData[card.dataset.task]; + if (!data) return; + + modalIcon.src = data.icon; + modalTitle.textContent = data.title; + + if (data.description) { + modalDescription.textContent = data.description; + modalDescription.style.display = ''; + } else { + modalDescription.style.display = 'none'; + } + + const steps = data.steps; + let sections = []; + let isFlat = false; + + if (Array.isArray(steps) && steps.length > 0) { + if (typeof steps[0] === 'object' && steps[0].steps && Array.isArray(steps[0].steps)) { + sections = steps; + } else { + sections = [{ title: null, steps: steps }]; + isFlat = true; + } + } else { + sections = [{ title: null, steps: [] }]; + isFlat = true; + } + + const renderSteps = (stepsArray) => { + modalSteps.innerHTML = stepsArray.map(step => `
  • ${step}
  • `).join(''); + }; + + if (sections.length > 1 && !isFlat) { + segContainer.style.display = 'flex'; + segContainer.innerHTML = ''; + + sections.forEach((section, index) => { + const btn = document.createElement('button'); + btn.className = 'segmented-btn' + (index === 0 ? ' active' : ''); + btn.textContent = section.title; + btn.dataset.index = index; + btn.addEventListener('click', () => { + segContainer.querySelectorAll('.segmented-btn').forEach(b => b.classList.remove('active')); + btn.classList.add('active'); + renderSteps(sections[index].steps); + }); + segContainer.appendChild(btn); + }); + + renderSteps(sections[0].steps); + } else { + segContainer.style.display = 'none'; + if (sections.length === 1) { + renderSteps(sections[0].steps); + } else { + renderSteps(steps); // Fallback + } + } + + modal.classList.add('active'); + }); + }); + + // Schließen + closeBtn?.addEventListener('click', () => modal.classList.remove('active')); + modal.addEventListener('click', (e) => { + if (e.target === modal) modal.classList.remove('active'); + }); +} +function setupPlacementViewer(placementData) { + let currentIndex = 0; + let showIncense = false; + + const placementImage = document.getElementById('placement-image'); + const placementLabel = document.getElementById('placement-label'); + const prevBtn = document.getElementById('placement-prev'); + const nextBtn = document.getElementById('placement-next'); + const weihrauchToggle = document.getElementById('incense-toggle'); + const koerbchenLegend = document.getElementById('basket-toggle'); + const weihrauchLegend = document.getElementById('incense-legend'); + const kerzeLabel = document.getElementById('kerze-label'); + + function updatePlacement() { + const dataset = showIncense ? placementData.withWeihrauch : placementData.standard; + const currentItem = dataset[currentIndex]; + + if (!currentItem) return; + + placementImage.src = currentItem.image; + placementLabel.textContent = currentItem.label; + kerzeLabel.textContent = showIncense ? 'Kerze & Kollekte' : 'Kerze'; + + const isEntranceWithIncense = showIncense && currentIndex === 0; + koerbchenLegend.style.display = isEntranceWithIncense ? 'none' : ''; + weihrauchLegend.style.display = isEntranceWithIncense ? '' : 'none'; + } + + prevBtn?.addEventListener('click', () => { + currentIndex = (currentIndex - 1 + 3) % 3; + updatePlacement(); + }); + + nextBtn?.addEventListener('click', () => { + currentIndex = (currentIndex + 1) % 3; + updatePlacement(); + }); + + weihrauchToggle?.addEventListener('click', () => { + showIncense = !showIncense; + weihrauchToggle.classList.toggle('active', showIncense); + updatePlacement(); + }); + + updatePlacement(); +} + +function getOstern(jahr) { + const a = jahr % 19; + const b = jahr % 4; + const c = jahr % 7; + const k = Math.floor(jahr / 100); + const p = Math.floor((13 + 8 * k) / 25); + const q = Math.floor(k / 4); + const M = (15 - p + k - q) % 30; + const N = (4 + k - q) % 7; + const d = (19 * a + M) % 30; + const e = (2 * b + 4 * c + 6 * d + N) % 7; + const days = 22 + d + e; + + if (days <= 31) { + return new Date(jahr, 2, days); + } else { + return new Date(jahr, 3, days - 31); + } +} + +function tageDifferenz(datum1, datum2) { + const day = 24 * 60 * 60 * 1000; + return Math.round((datum1 - datum2) / day); +} + +function getMiniColor(targetDate) { + const year = targetDate.getFullYear(); + const easter = getOstern(year); + const diffToEaster = tageDifferenz(targetDate, easter); + + const month = targetDate.getMonth(); + const day = targetDate.getDate(); + const wochentag = targetDate.getDay(); + + if ((month === 11 && day >= 24) || (month === 0 && day <= 10)) { + return "GRÜN"; + } + + if (diffToEaster >= -46 && diffToEaster <= -1) { + if (diffToEaster === -7 || diffToEaster === -2) { + return "ROT"; + } + return "LILA"; + } + + if (diffToEaster >= 0 && diffToEaster <= 50) { + if (diffToEaster === 50) { + return "ROT"; + } + return "GRÜN"; + } + + let holyEvening = new Date(year, 11, 24); + let fourthAdvent = new Date(year, 11, 24 - holyEvening.getDay()); + let firstAdvent = new Date(fourthAdvent); + firstAdvent.setDate(fourthAdvent.getDate() - 21); + + if (targetDate >= firstAdvent && targetDate < holyEvening) { + return "LILA"; + } + + return "GRÜN"; +} + +function getMiniAmount(targetDate) { + const amount = (targetDate.getDay() === 0 || targetDate.getDay() === 6) ? 6 : 4; + + return amount; +} \ No newline at end of file diff --git a/mini/pages/knowledge.html b/mini/pages/knowledge.html new file mode 100644 index 0000000..09d71b7 --- /dev/null +++ b/mini/pages/knowledge.html @@ -0,0 +1,13 @@ +
    +
    + + +
    +
    +
    +
    + +
    +
    +
    +
    \ No newline at end of file diff --git a/mini/pages/sequence.html b/mini/pages/sequence.html new file mode 100644 index 0000000..e4030a7 --- /dev/null +++ b/mini/pages/sequence.html @@ -0,0 +1,20 @@ +
    +
    + Aufgaben: +
    + + + + + + +
    +
    +
    + + +
    \ No newline at end of file diff --git a/mini/pages/today.html b/mini/pages/today.html new file mode 100644 index 0000000..b58c1e1 --- /dev/null +++ b/mini/pages/today.html @@ -0,0 +1,161 @@ +
    +
    +
    + Sonntag + 15. August 2026 +
    +
    +
    + Shirt Icon +
    + Gewand + Grün +
    +
    +
    + Users Icon +
    + Ministranten + 4 +
    +
    +
    +
    +
    +
    +

    Gewand

    +
    +

    + Die Gewandfarbe richtet sich nach dem Kirchenjahr. Ihr übernehmt einfach die Farbe, die auch der Pfarrer + trägt.
    + (Bei Weiß oder Gold zieht ihr stattdessen Grün an.) +

    +
    +
    +
    + Shirt Icon +
    +
    + Grünes Gewand + An Tagen im Jahreskreis +
    +
    +
    +
    + Shirt Icon +
    +
    + Lilanes Gewand + An Tagen der Buße und Fastenzeit +
    +
    +
    +
    + Shirt Icon +
    +
    + Rotes Gewand + An Festtagen des Heiligen Geistes und der Märtyrer +
    +
    +
    +
    + +
    +
    +

    Sitzordnung

    + +
    + +
    + Platzierung Altar + Platzierung Einzug +
    + +
    + + Einzug + +
    + +
    +
    Gabenbereitung Icon Gabenbereitung
    +
    Kerze Icon +

    Kerze

    +
    +
    Kollekte Icon + Kollekte
    +
    Weihrauch Icon Weihrauch
    +
    +
    +
    +
    + File Icon +
    + Kurzübersicht + Gewand, Platzierungen & Aufgaben als PDF +
    +
    + + Download Icon PDF herunterladen + +
    + +
    +

    Deine Aufgaben

    +
    + + + + +
    +
    + + + +
    \ No newline at end of file diff --git a/mini/style.css b/mini/style.css new file mode 100644 index 0000000..05e0613 --- /dev/null +++ b/mini/style.css @@ -0,0 +1,1538 @@ +/* Grundlegendes Setup */ +:root { + /* ===== App‑Farben ===== */ + --primary: #1b4332; + /* dunkles Grün – Hauptfarbe */ + --primary-light: #d8f3dc; + /* sehr helles Grün – Flächen */ + --primary-dark: #081c15; + /* noch dunkleres Grün – Hover */ + --primary-rgb: 27, 67, 50; + /* RGB für rgba() */ + --primary-soft: rgba(27, 67, 50, 0.12); + + --accent: #cb6b2f; + /* warmes Orange – Akzente, Highlights */ + --accent-rgb: 203, 107, 47; + + /* ===== Neutrale Farben ===== */ + --bg: #f7fafc; + /* Seitenhintergrund */ + --surface: #ffffff; + /* Kartenhintergrund */ + --surface-glass: rgba(255, 255, 255, 0.78); + --text: #1a1a2e; + /* Haupttext */ + --text-muted: #6b6b7b; + /* Sekundärtext */ + --text-inverse: #ffffff; + --border: rgba(0, 0, 0, 0.07); + --border-strong: rgba(0, 0, 0, 0.12); + + /* ===== Liturgische Farben ===== */ + --liturgical-green: #4caf50; + --liturgical-red: #f44336; + --liturgical-purple: #9c27b0; + --liturgical-white: #ffffff; + --liturgical-gold: #ffd700; + + /* ===== Schatten ===== */ + --shadow-sm: 0 2px 8px rgba(0, 0, 0, 0.06); + --shadow-md: 0 8px 32px rgba(0, 0, 0, 0.1); + --shadow-lg: 0 16px 48px rgba(0, 0, 0, 0.15); + + /* ===== Radien ===== */ + --radius-sm: 10px; + --radius-md: 14px; + --radius-lg: 18px; + --radius-xl: 24px; + + /* ===== Typografie & Effekte ===== */ + --font: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; + --transition-fast: 0.18s cubic-bezier(0.4, 0, 0.2, 1); + --transition-slow: 0.35s cubic-bezier(0.4, 0, 0.2, 1); + + /* ===== Safe Area ===== */ + --safe-bottom: env(safe-area-inset-bottom, 0px); +} + +* { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +/* ===== BASIS ===== */ +body { + font-family: var(--font); + background-color: var(--bg); + /* Hier war #f7fafc hardcodiert! */ + color: var(--text); + /* Hier war #2d3748 hardcodiert! */ + display: flex; + flex-direction: column; + min-height: 100vh; + padding-bottom: 70px; + transition: background-color var(--transition-slow), color var(--transition-slow); +} + +/* OBERE LEISTE (Top Bar) */ +.top-bar { + background-color: var(--surface); + /* Vorher #ffffff */ + border-bottom: 1px solid var(--border); + /* Vorher #e2e8f0 */ + padding: 12px 20px; + display: flex; + justify-content: space-between; + align-items: center; + position: sticky; + top: 0; + z-index: 1000; + transition: background-color var(--transition-slow), border-color var(--transition-slow); +} + +.header-icon { + width: 36px; + height: 36px; + border-radius: 10px; + background: var(--primary); + color: #fff; + display: flex; + align-items: center; + justify-content: center; + box-shadow: 0 4px 12px rgba(var(--primary-rgb), 0.35); + transition: background-color var(--transition-slow), box-shadow var(--transition-slow); +} + +.header-icon img { + width: 25px; + height: 30px; + /*invert colors for dark icon on light background*/ + filter: invert(100%); +} + +.header-title { + font-size: 0.9rem; + font-weight: 700; + letter-spacing: -0.01em; + color: var(--text); +} + +.header-subtitle { + font-size: 0.7rem; + color: var(--text-muted); + font-weight: 500; + letter-spacing: 0.02em; + text-transform: uppercase; +} + +.brand { + display: flex; + align-items: center; + gap: 10px; +} + +.app-name { + font-size: 1.1rem; + font-weight: 700; + color: #1a365d; +} + +.location { + font-size: 0.75rem; + color: #718096; +} + +/* DESKTOP NAVIGATION (Standardmäßig ausgeblendet) */ +/* DESKTOP NAVIGATION */ +.desktop-nav { + display: none; + gap: 6px; + align-items: center; + position: absolute; + left: 50%; + transform: translateX(-50%); +} + +.desktop-nav .tab-btn { + display: inline-block; + padding: 8px 18px; + border-radius: var(--radius-sm); + font-size: 0.9rem; + font-weight: 500; + color: var(--text-muted); + text-decoration: none; + background: transparent; + border: none; + cursor: pointer; + transition: all var(--transition-fast); + position: relative; + letter-spacing: 0.01em; +} + +.desktop-nav .tab-btn:hover { + color: var(--text); + background: rgba(0, 0, 0, 0.04); +} + +.desktop-nav .tab-btn.active { + color: var(--primary); + background: var(--primary-soft); + font-weight: 600; +} + +/* Optional: Unterstrich-Effekt für aktive Tabs */ +.desktop-nav .tab-btn.active::after { + content: ''; + position: absolute; + bottom: 2px; + left: 50%; + transform: translateX(-50%); + width: 20px; + height: 3px; + border-radius: 2px; + background: var(--primary); +} + +/* INHALTSBEREICH */ +.content { + flex: 1; + padding: 20px; + max-width: 900px; + margin: 0 auto; + width: 100%; +} + +.tab-section { + background: #ffffff; + padding: 24px; + border-radius: 12px; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05); +} + +.tab-section h2 { + margin-bottom: 12px; + color: #1a365d; +} + +/* MOBILE BOTTOM NAVIGATION (Standardmäßig angezeigt) */ +.bottom-nav { + display: flex; + position: fixed; + bottom: 0; + left: 0; + width: 100%; + background-color: var(--surface); + /* Vorher #ffffff */ + border-top: 1px solid var(--border); + /* Vorher #e2e8f0 */ + height: 60px; + z-index: 1000; + justify-content: space-around; + align-items: center; + transition: background-color var(--transition-slow), border-color var(--transition-slow); +} + +.bottom-tab { + display: flex; + flex-direction: column; + align-items: center; + text-decoration: none; + color: var(--text-muted); + font-size: 0.7rem; + flex: 1; + transition: color var(--transition-fast); +} + +.bottom-tab .icon { + font-size: 1.25rem; + margin-bottom: 2px; +} + +.bottom-tab.active { + color: var(--primary); + font-weight: 600; +} + +.settings-btn { + background: none; + border: none; + cursor: pointer; + padding: 6px; + margin-left: 12px; + border-radius: 50%; + width: 40px; + height: 40px; + display: grid; + place-items: center; + transition: background var(--transition-fast); + flex-shrink: 0; +} + +.settings-btn:hover { + background: rgba(0, 0, 0, 0.06); +} + +.settings-btn img { + width: 24px; + height: 24px; + filter: invert(0.4); +} + +/* ===== Settings-Modal spezifische Anpassungen ===== */ +.settings-modal .modal-icon { + background: var(--primary-soft); + color: var(--primary); +} + +.settings-modal .modal-icon img { + filter: none; + /* Icon soll originalfarben bleiben */ +} + +/* Einstellungs-Items */ +.setting-item { + display: flex; + justify-content: space-between; + align-items: center; + gap: 16px; + padding: 12px 0; +} + +.setting-label { + display: flex; + flex-direction: column; + gap: 2px; +} + +.setting-label span:first-child { + font-weight: 600; + color: var(--text); +} + + +/* Toggle Switch */ +.switch { + position: relative; + display: inline-block; + width: 48px; + height: 26px; + flex-shrink: 0; +} + +.switch input { + opacity: 0; + width: 0; + height: 0; +} + +.slider { + position: absolute; + cursor: pointer; + inset: 0; + background-color: #ccc; + transition: var(--transition-fast); + border-radius: 26px; +} + +.slider::before { + content: ""; + position: absolute; + height: 20px; + width: 20px; + left: 3px; + bottom: 3px; + background-color: white; + transition: var(--transition-fast); + border-radius: 50%; +} + +.switch input:checked+.slider { + background-color: var(--primary); +} + +.switch input:checked+.slider::before { + transform: translateX(22px); +} + +/* Trennlinie */ +.settings-divider { + border: none; + border-top: 1px solid var(--border); + margin: 8px 0 12px; +} + +/* Info-Block */ +.settings-info { + display: grid; + gap: 6px; + font-size: 0.95rem; + color: var(--text); +} + +.settings-info a { + color: var(--primary); + text-decoration: none; + font-weight: 500; +} + +.settings-info a:hover { + text-decoration: underline; +} + +/* ===== Heute‑Dashboard: Allgemeine Struktur ===== */ +.today-dashboard { + display: flex; + flex-direction: column; + gap: 24px; +} + +/* ===== Basis Karte (bereits vorhanden, aber angepasst) ===== */ +.card { + background: var(--surface-glass); + border: 1px solid var(--border); + border-radius: var(--radius-md); + box-shadow: var(--shadow-sm); + backdrop-filter: blur(18px); + -webkit-backdrop-filter: blur(18px); + padding: 20px; + transition: box-shadow var(--transition-fast); +} + +.card:hover { + box-shadow: var(--shadow-md); +} + +/* Top‑Zusammenfassung */ +.today-summary-card { + display: flex; + flex-direction: column; + gap: 16px; + background: var(--surface-glass); + border: 1px solid var(--border); + border-radius: var(--radius-md); + box-shadow: var(--shadow-sm); + backdrop-filter: blur(18px); + -webkit-backdrop-filter: blur(18px); + padding: 20px; +} + +.summary-date { + display: flex; + flex-direction: column; + gap: 2px; +} + +.summary-date #weekday { + font-size: 1.4rem; + font-weight: 700; + color: var(--text); +} + +.summary-date #date { + font-size: 0.9rem; + color: var(--text-muted); +} + +.summary-details { + display: flex; + flex-wrap: wrap; + gap: 16px; +} + +.summary-item { + display: flex; + align-items: center; + gap: 10px; + flex: 1; + min-width: 140px; +} + +.summary-item img { + width: 30px; + height: 30px; + color: var(--primary); +} + +.summary-item strong { + display: block; + font-size: 0.85rem; + color: var(--text-muted); +} + +.summary-item span { + display: block; + font-size: 1.2rem; + font-weight: 600; + color: var(--text); +} + +/* Platzierungs‑Viewer */ +.placement-card { + display: flex; + flex-direction: column; + gap: 16px; + background: var(--surface-glass); + border: 1px solid var(--border); + border-radius: var(--radius-md); + box-shadow: var(--shadow-sm); + backdrop-filter: blur(18px); + -webkit-backdrop-filter: blur(18px); + padding: 20px; +} + +.placement-header { + display: flex; + justify-content: space-between; + align-items: center; +} + +.placement-header h3 { + margin: 0; + font-size: 1.3rem; + color: var(--text); +} + +.placement-toggle { + display: flex; + align-items: center; + gap: 6px; + padding: 8px 14px; + border-radius: 999px; + border: 1px solid var(--border); + background: var(--surface); + color: var(--text); + font-size: 0.85rem; + cursor: pointer; + transition: all var(--transition-fast); +} + +.placement-toggle.active { + background: var(--primary); + color: var(--text-inverse); + border-color: var(--primary); +} + +.placement-toggle img { + width: 20px; + height: 20px; +} + +.placement-toggle.active img { + width: 20px; + height: 20px; + filter: invert(100%); +} + +.placement-stage { + width: 100%; + aspect-ratio: 14 / 9; + background: rgba(0, 0, 0, 0.03); + border-radius: var(--radius-sm); + overflow: hidden; + position: relative; +} + +.placement-stage img { + width: 100%; + height: 100%; + object-fit: contain; + transition: opacity var(--transition-fast); + position: absolute; + top: 0; + left: 0; +} + +.placement-controls { + display: flex; + justify-content: center; + align-items: center; + gap: 16px; +} + +.placement-controls button { + width: 40px; + height: 40px; + border-radius: 50%; + border: 1px solid var(--border); + background: var(--surface); + display: grid; + place-items: center; + cursor: pointer; + transition: background var(--transition-fast); +} + +.placement-controls button:hover { + background: rgba(0, 0, 0, 0.05); +} + +.placement-controls img { + width: 20px; + height: 20px; +} + +#placement-label { + font-weight: 600; + font-size: 0.9rem; + color: var(--text); + min-width: 80px; + text-align: center; +} + +.placement-legend { + display: flex; + flex-wrap: wrap; + gap: 20px; + justify-content: center; +} + +.legend-item { + display: flex; + align-items: center; + gap: 6px; + font-size: 0.8rem; + color: var(--text-muted); +} + +/* ===== Liturgische Karte (Gewand) ===== */ +.liturgical-card { + display: flex; + flex-direction: column; + gap: 16px; +} + +.liturgical-title-row { + display: flex; + justify-content: space-between; + align-items: center; + gap: 12px; +} + +.liturgical-title-row h3 { + margin: 0; + font-size: 22px; + letter-spacing: -0.03em; + color: var(--text); +} + +.info-stack { + display: grid; + gap: 9px; +} + +.info-line { + display: flex; + gap: 10px; + align-items: flex-start; + padding: 11px; + border-radius: 13px; + background: rgba(255, 255, 255, 0.62); + border: 1px solid rgba(255, 255, 255, 0.65); +} + +.info-line-icon { + width: 34px; + height: 34px; + border-radius: 11px; + display: grid; + place-items: center; + color: var(--text-inverse); + background: var(--liturgical-green); + flex: 0 0 auto; +} + +.info-line-icon-green { + background: var(--liturgical-green); +} + +.info-line-icon-lilac { + background: var(--liturgical-purple); +} + +.info-line-icon-red { + background: var(--liturgical-red); +} + + +.info-line-icon img { + width: 25px; + height: 25px; +} + +.info-line strong { + display: block; + margin-bottom: 2px; + font-size: 0.85rem; + color: var(--text); +} + +.info-line span { + display: block; + color: var(--text-muted); + font-size: 0.75rem; +} + + +.card-header { + display: flex; + justify-content: space-between; + align-items: center; +} + +.card-header h3 { + margin: 0; + font-size: 20px; + letter-spacing: -0.02em; + color: var(--text); +} + + +.card-hint { + font-size: 0.8rem; + color: var(--text-muted); + text-align: center; + margin: 0; +} + +/* ===== Aufgaben‑Bereich ===== */ +.tasks-section { + display: flex; + flex-direction: column; + gap: 16px; +} + +.tasks-section h2 { + font-size: 1.5rem; + letter-spacing: -0.02em; + color: var(--text); + margin: 0; +} + +.tasks-grid { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 12px; +} + +@media (min-width: 480px) { + .tasks-grid { + gap: 16px; + } +} + +.task-card { + position: relative; + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 12px; + padding: 18px; + background: var(--surface-glass); + border: 1px solid var(--border); + border-radius: var(--radius-md); + box-shadow: var(--shadow-sm); + backdrop-filter: blur(18px); + -webkit-backdrop-filter: blur(18px); + cursor: pointer; + transition: transform var(--transition-fast), + box-shadow var(--transition-fast), + border-color var(--transition-fast); + text-align: left; + font-family: inherit; +} + +.task-card:hover { + transform: translateY(-2px); + box-shadow: var(--shadow-md); + border-color: var(--border-strong); +} + +.task-card:active { + transform: translateY(0); + box-shadow: var(--shadow-sm); +} + +.task-card img { + width: 28px; + height: 28px; + color: var(--primary); +} + +.task-name { + font-weight: 600; + font-size: 1rem; + color: var(--text); +} + +.task-arrow { + position: absolute; + top: 12px; + right: 12px; + display: grid; + place-items: center; + width: 28px; + height: 28px; + border-radius: 50%; + background: rgba(0, 0, 0, 0.04); + transition: background var(--transition-fast); +} + +.task-arrow img { + width: 13px; + height: 13px; +} + +.task-card:hover .task-arrow { + background: rgba(0, 0, 0, 0.08); +} + +/* ===== Modal ===== */ +.modal-overlay { + position: fixed; + inset: 0; + z-index: 2067; + display: flex; + align-items: center; + justify-content: center; + padding: 20px; + background: rgba(0, 0, 0, 0.5); + opacity: 0; + pointer-events: none; + transition: opacity var(--transition-slow); +} + +.modal-overlay.active { + opacity: 1; + pointer-events: auto; +} + +.modal-content { + position: relative; + width: 100%; + max-width: 560px; + max-height: 85vh; + background: var(--surface); + border-radius: var(--radius-xl); + box-shadow: var(--shadow-lg); + padding: 32px 32px 24px; + display: flex; + flex-direction: column; + gap: 20px; + transform: translateY(20px); + transition: transform var(--transition-slow); +} + +@media (min-width: 768px) { + .modal-content { + max-width: 640px; + padding: 40px 40px 28px; + gap: 24px; + border-radius: calc(var(--radius-xl) * 1.2); + } + + .modal-title { + font-size: 1.75rem; + } + + .modal-description { + font-size: 1.05rem; + } + + .modal-steps { + max-height: 360px; + font-size: 1rem; + gap: 12px; + padding-left: 24px; + } + + .modal-icon { + width: 64px; + height: 64px; + border-radius: 20px; + } + + .modal-icon img { + width: 36px; + height: 36px; + } + + .modal-close { + width: 40px; + height: 40px; + } + + .modal-close img { + width: 20px; + height: 20px; + } +} + +/* Noch großzügiger auf großen Bildschirmen */ +@media (min-width: 1200px) { + .modal-content { + max-width: 720px; + padding: 48px 48px 32px; + gap: 28px; + } + + .modal-title { + font-size: 2rem; + } + + .modal-description { + font-size: 1.1rem; + } + + .modal-steps { + max-height: 400px; + font-size: 1.05rem; + gap: 14px; + } + + .modal-icon { + width: 72px; + height: 72px; + border-radius: 24px; + } + + .modal-icon img { + width: 40px; + height: 40px; + } +} + +.modal-close { + top: 12px; + right: 12px; + margin-left: auto; + width: 36px; + height: 36px; + border-radius: 50%; + border: none; + background: rgba(0, 0, 0, 0.04); + display: grid; + place-items: center; + cursor: pointer; + transition: background var(--transition-fast); + flex-shrink: 0; + z-index: 1; +} + +.modal-header { + flex-shrink: 0; + display: flex; + flex-direction: column; + gap: 12px; +} + +.modal-top-row { + display: flex; + justify-content: space-between; + align-items: center; +} + +.modal-close:hover { + background: rgba(0, 0, 0, 0.08); +} + +.modal-close img { + width: 18px; + height: 18px; +} + +.modal-icon { + width: 56px; + height: 56px; + border-radius: 16px; + background: var(--primary-soft); + display: grid; + place-items: center; + color: var(--primary); + flex-shrink: 0; +} + +.modal-icon img { + width: 32px; + height: 32px; +} + +.task-icon { + width: 20px; + height: 20px; +} + +.modal-title { + font-size: 1.5rem; + font-weight: 700; + letter-spacing: -0.02em; + color: var(--text); + margin: 0; + flex-shrink: 0; +} + +.modal-description { + font-size: 0.95rem; + color: var(--text-muted); + line-height: 1.5; + margin: 0; + flex-shrink: 0; +} + +.modal-steps { + flex: 1; + overflow-y: auto; + max-height: 300px; + margin: 0; + padding-left: 20px; + list-style: none; + padding-right: 4px; + display: grid; + gap: 8px; + font-size: 0.9rem; + color: var(--text); +} + +.modal-steps li { + line-height: 1.5; +} + +.modal-steps::-webkit-scrollbar { + width: 6px; +} + +.modal-steps::-webkit-scrollbar-track { + background: rgba(0, 0, 0, 0.05); + border-radius: 3px; +} + +.modal-steps::-webkit-scrollbar-thumb { + background: rgba(0, 0, 0, 0.2); + border-radius: 3px; +} + +.modal-steps::-webkit-scrollbar-thumb:hover { + background: rgba(0, 0, 0, 0.3); +} + +.segmented-control { + display: flex; + background: var(--border); + border-radius: var(--radius-sm); + padding: 4px; + margin-bottom: 16px; + position: relative; + border: 1px solid var(--border); +} + +.segmented-btn { + flex: 1; + padding: 10px 12px; + border-radius: 8px; + border: none; + background: transparent; + font-size: 0.8rem; + font-weight: 600; + cursor: pointer; + font-family: var(--font); + color: var(--text-muted); + min-height: 42px; + transition: all var(--transition-fast); + position: relative; + z-index: 1; + -webkit-user-select: none; + user-select: none; +} + +.segmented-btn.active { + color: var(--text); + background: var(--surface); + box-shadow: var(--shadow-sm); + font-weight: 700; +} + +body.dark-mode .segmented-control { + background: var(--surface-2); + border-color: var(--border-strong); +} + +body.dark-mode .segmented-btn.active { + background: var(--surface-glass); + color: var(--text); +} + +/* ===== Filter‑Leiste ===== */ +.filter-bar { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 8px; + margin-bottom: 20px; +} + +.filter-label { + font-weight: 600; + font-size: 0.85rem; + color: var(--text-muted); + text-transform: uppercase; + letter-spacing: 0.04em; + margin-right: 4px; +} + +.filter-chips { + display: flex; + flex-wrap: wrap; + gap: 8px; +} + +.filter-chip { + padding: 8px 16px; + border-radius: 999px; + border: 1px solid var(--border); + background: var(--surface-glass); + color: var(--text); + font-size: 0.85rem; + cursor: pointer; + transition: all var(--transition-fast); + font-family: inherit; +} + +.filter-chip:hover { + border-color: var(--border-strong); + background: var(--surface); +} + +.filter-chip.active { + background: var(--primary); + color: var(--text-inverse); + border-color: var(--primary); + box-shadow: 0 4px 12px rgba(var(--primary-rgb), 0.3); +} + +/* ===== Ablauf‑Liste (Akkordeon) ===== */ +.sequence-list { + display: flex; + flex-direction: column; + gap: 10px; +} + +.ablauf-item { + background: var(--surface-glass); + border: 1px solid var(--border); + border-radius: var(--radius-md); + box-shadow: var(--shadow-sm); + backdrop-filter: blur(18px); + -webkit-backdrop-filter: blur(18px); + overflow: hidden; + transition: box-shadow var(--transition-fast); +} + +.ablauf-item:hover { + box-shadow: var(--shadow-md); +} + +.sequence-header { + display: flex; + justify-content: space-between; + align-items: center; + width: 100%; + padding: 16px 20px; + background: transparent; + border: none; + cursor: pointer; + font-family: inherit; + font-size: 1rem; + font-weight: 600; + color: var(--text); + text-align: left; + transition: background var(--transition-fast); +} + +.sequence-header:hover { + background: rgba(255, 255, 255, 0.4); +} + +.sequence-title { + flex: 1; +} + +.ablauf-arrow { + transition: transform var(--transition-fast); +} + +.ablauf-item.active .ablauf-arrow { + transform: rotate(180deg); +} + +.sequence-content { + max-height: 0; + overflow: hidden; + transition: max-height var(--transition-slow), padding var(--transition-slow); + padding: 0 20px; + color: var(--text-muted); + font-size: 0.9rem; + line-height: 1.5; +} + +.ablauf-item.active .sequence-content { + max-height: 500px; + /* ausreichend für längere Texte */ + padding: 0 20px 20px; +} + +/* ===== Wissens‑Seite: Tabs ===== */ +.wissen-tabs { + display: flex; + gap: 10px; + margin-bottom: 20px; + flex-wrap: wrap; +} + +.wissen-tab { + padding: 10px 20px; + border-radius: 999px; + border: 1px solid var(--border); + background: var(--surface-glass); + color: var(--text); + font-size: 0.9rem; + font-weight: 600; + cursor: pointer; + transition: all var(--transition-fast); + font-family: inherit; +} + +.wissen-tab:hover { + background: var(--surface); + border-color: var(--border-strong); +} + +.wissen-tab.active { + background: var(--primary); + color: var(--text-inverse); + border-color: var(--primary); + box-shadow: 0 4px 12px rgba(var(--primary-rgb), 0.3); +} + +/* ===== Wissens‑Panel (Tab‑Inhalt) ===== */ +.wissen-panel { + display: none; +} + +.wissen-panel.active { + display: block; +} + +/* ===== Wissens‑Liste & Akkordeon‑Einträge ===== */ +.wissen-list { + display: flex; + flex-direction: column; + gap: 10px; +} + +.wissen-item { + background: var(--surface-glass); + border: 1px solid var(--border); + border-radius: var(--radius-md); + box-shadow: var(--shadow-sm); + backdrop-filter: blur(18px); + -webkit-backdrop-filter: blur(18px); + overflow: hidden; + transition: box-shadow var(--transition-fast); +} + +.wissen-item:hover { + box-shadow: var(--shadow-md); +} + +.wissen-header { + display: flex; + justify-content: space-between; + align-items: center; + width: 100%; + padding: 16px 20px; + background: transparent; + border: none; + cursor: pointer; + font-family: inherit; + font-size: 1rem; + font-weight: 600; + color: var(--text); + text-align: left; + transition: background var(--transition-fast); +} + +.wissen-header:hover { + background: rgba(255, 255, 255, 0.4); +} + +.wissen-arrow { + transition: transform var(--transition-fast); + width: 12px; + height: 12px; +} + +.wissen-item.active .wissen-arrow { + transform: rotate(180deg); +} + +.wissen-content { + max-height: 0; + overflow: hidden; + transition: max-height var(--transition-slow), padding var(--transition-slow); + padding: 0 20px; + color: var(--text-muted); + font-size: 0.9rem; + line-height: 1.6; +} + +.wissen-item.active .wissen-content { + max-height: 1000px; + /* großzügig für längere Texte */ + padding: 0 20px 20px; +} + +/* ===== Formatierte Dialoge innerhalb der Wissens‑Inhalte ===== */ +.dialog-box { + background: rgba(255, 255, 255, 0.6); + border-left: 4px solid var(--primary); + padding: 12px 16px; + margin-top: 10px; + border-radius: 0 var(--radius-sm) var(--radius-sm) 0; + font-size: 0.9rem; + line-height: 1.5; + color: var(--text); +} + +.dialog-box p { + margin: 0 0 8px; +} + +.dialog-box p:last-child { + margin-bottom: 0; +} + +.dialog-speaker { + font-weight: 600; + color: var(--primary); +} + +/* ===== Zusätzliche Textformatierungen ===== */ +.wissen-content strong { + color: var(--text); +} + +.wissen-content em { + color: var(--text-muted); +} + +/* ===== PDF-Download (Heute) ===== */ +.pdf-download-card { + display: flex; + flex-direction: column; + gap: 14px; + padding: 16px 20px; +} + +.pdf-download-content { + display: flex; + align-items: center; + gap: 12px; +} + +.pdf-download-content img { + width: 24px; + height: 24px; + color: var(--primary); +} + +.pdf-download-content strong { + display: block; + font-size: 1rem; + color: var(--text); +} + +.pdf-download-content span { + font-size: 0.8rem; + color: var(--text-muted); +} + +/* ===== Download-Button (allgemein) ===== */ +.btn-download { + display: flex; + align-items: center; + justify-content: center; + gap: 8px; + padding: 10px 18px; + border-radius: var(--radius-sm); + background: var(--primary); + color: var(--text-inverse); + text-decoration: none; + font-weight: 600; + font-size: 0.9rem; + transition: background var(--transition-fast), + box-shadow var(--transition-fast); + border: none; + cursor: pointer; + text-align: center; +} + +.btn-download:hover { + background: var(--primary-dark); + box-shadow: 0 4px 12px rgba(var(--primary-rgb), 0.3); +} + +.btn-download img { + width: 18px; + height: 18px; + filter: invert(100%); +} + +/* ===== Download-Button in Ablauf-Seite ===== */ +.ablauf-download-bar { + position: fixed; + left: 0; + right: 0; + z-index: 999; + /* unterhalb der Top‑Bar (1000) und Bottom‑Nav (1000) */ + + padding: 12px 20px; + background: var(--surface-glass); + backdrop-filter: blur(18px); + -webkit-backdrop-filter: blur(18px); + border-top: 1px solid var(--border); + box-shadow: 0 -4px 12px rgba(0, 0, 0, 0.05); + + display: flex; + justify-content: center; +} + +@media (min-width: 768px) { + .ablauf-download-bar { + bottom: 0; + } +} + +@media (max-width: 767px) { + .ablauf-download-bar { + bottom: calc(60px + env(safe-area-inset-bottom, 0px)); + } +} + +.btn-download-large { + width: 100%; + padding: 14px 20px; + font-size: 1rem; + border-radius: var(--radius-md); +} + +@media (min-width: 768px) { + body { + padding-bottom: 0; + } + + .header-title { + font-size: 1.05rem; + } + + .desktop-nav { + display: flex; + } + + .bottom-nav { + display: none; + } +} + +body.dark-mode { + --bg: #121415; + + --surface: #1a1d1e; + --surface-glass: rgba(26, 29, 30, 0.85); + --surface-2: #24282a; + + --text: #e2e8f0; + --text-muted: #94a3b8; + --text-inverse: #ffffff; + + /* Rahmen */ + --border: rgba(255, 255, 255, 0.08); + --border-strong: rgba(255, 255, 255, 0.15); + + --primary: #2d6a4f; + --primary-soft: rgba(45, 106, 79, 0.25); + + --shadow-sm: 0 4px 12px rgba(0, 0, 0, 0.4); + --shadow-md: 0 8px 32px rgba(0, 0, 0, 0.6); + --shadow-lg: 0 16px 48px rgba(0, 0, 0, 0.8); +} + +body.dark-mode .modal-content { + background: var(--surface-2); + border: 1px solid var(--border-strong); +} + +body.dark-mode .modal-close { + background: rgba(255, 255, 255, 0.08); +} + +body.dark-mode .modal-close:hover { + background: rgba(255, 255, 255, 0.15); +} + +body.dark-mode .slider { + background-color: #3d444d; +} + +body.dark-mode .switch input:checked+.slider { + background-color: var(--primary); +} + +body.dark-mode .settings-info a { + color: #4ade80; +} + +body.dark-mode .info-line { + background: rgba(255, 255, 255, 0.03); +} + +body.dark-mode .filter-chip, +body.dark-mode .placement-toggle { + background: var(--surface-2); + border-color: var(--border); +} + +body.dark-mode .filter-chip.active, +body.dark-mode .placement-toggle.active { + background: var(--primary); + border-color: var(--primary); + color: var(--text-inverse); +} + +body.dark-mode .placement-stage { + background: rgba(255, 255, 255, 0.04); +} + +body.dark-mode .task-card img, +body.dark-mode .summary-item img, +body.dark-mode .info-line-icon img, +body.dark-mode .placement-toggle img, +body.dark-mode .modal-icon img, +body.dark-mode .pdf-download-content img, +body.dark-mode .bottom-tab img, +body.dark-mode .settings-btn img, +body.dark-mode .placement-controls img, +body.dark-mode .modal-close img, +body.dark-mode .ablauf-arrow img, +body.dark-mode .wissen-arrow img, +body.dark-mode .legend-item img, +body.dark-mode .filter-chip img, +body.dark-mode .sequence-header img, +body.dark-mode .wissen-header img, +body.dark-mode .task-arrow img { + filter: invert(0.9) brightness(1.2); +} + +body.dark-mode .placement-toggle.active img, +body.dark-mode .placement-stage img { + filter: none !important; +} \ No newline at end of file diff --git a/play/Tiny-Snake/snake.html b/play/Tiny-Snake/snake.html deleted file mode 100644 index 8b72601..0000000 --- a/play/Tiny-Snake/snake.html +++ /dev/null @@ -1,7 +0,0 @@ - -Snake - \ No newline at end of file diff --git a/projects.html b/projects.html new file mode 100644 index 0000000..ed67ede --- /dev/null +++ b/projects.html @@ -0,0 +1,175 @@ + + + + + + + BR0TCRAFT | Projekte + + + + + + + + + +
    +
    +

    Projects

    +

    + Dinge, die ich gebaut habe oder noch baue. +

    + +
    + + + + + +
    +
    + Snake +
    +
    + abgeschlossen +

    Tiny Snake

    +

    Ein Rewrite des Klassikers. Hauptsächlich um C++ besser zu lernen.

    +
    C++
    + +
    +
    + +
    +
    + in Arbeit +
    +
    + ⧖ in Arbeit +

    Demnächst

    +

    Weitere Projekte folgen. Kein ETA.

    +
    +
    + +
    +
    +
    + + + + \ No newline at end of file diff --git a/style.css b/style.css index e24829b..d0ac5fd 100644 --- a/style.css +++ b/style.css @@ -1,161 +1,1079 @@ -@font-face { - font-family: 'Orbitron'; - src: url('/fonts/Orbitron-Black.ttf') format('truetype'); - } - @font-face { - font-family: 'Roboto'; - src: url('/fonts/Roboto-Regular.ttf') format('truetype'); - } - body { - font-family: 'Roboto', sans-serif; - background-color: #120000; - color: #ffb3b3; +/* --- VARIABLES & RESET --- */ +:root { + --bg-body: #0a0505; + --bg-card: #140a0a; + --bg-glass: rgba(20, 10, 10, 0.85); + /* Etwas dunkler für bessere Lesbarkeit */ + + --accent-primary: #ff4d4d; + --accent-dim: #661f1f; + --accent-glow: rgba(255, 77, 77, 0.2); + + --text-main: #e0e0e0; + --text-muted: #a0a0a0; + + --font-display: 'Orbitron', sans-serif; + --font-body: 'Inter', sans-serif; + --font-code: 'Fira Code', monospace; + + --spacing-container: max(20px, 5vw); + --radius: 12px; +} + +* { + box-sizing: border-box; margin: 0; + padding: 0; +} + +body { + background-color: var(--bg-body); + color: var(--text-main); + font-family: var(--font-body); + line-height: 1.6; overflow-x: hidden; - } - .header { - background-color: #330000; - padding: 20px; - text-align: center; - border-bottom: 5px solid #660000; + padding-top: 80px; + /* Platz für fixed navbar */ +} + +a { + text-decoration: none; + color: inherit; + transition: 0.2s; +} + +ul { + list-style: none; +} + +img { + max-width: 100%; + display: block; + border-radius: var(--radius); +} + +/* --- NAVIGATION --- */ +.navbar { position: fixed; top: 0; + left: 0; width: 100%; - z-index: 10; - } - .header h1 { - margin: 0; - font-size: 2.5em; - font-family: 'Orbitron', sans-serif; - color: #ffffff; - text-shadow: 0px 2px 5px rgba(255, 0, 0, 0.5); - } - .header-image { - width: 100%; - overflow: hidden; - margin-top: 90px; - } - .header-image img { - width: 100%; - height: auto; - } - .contact-icons { - display: flex; - justify-content: center; - padding: 20px; - } - .contact-icons img { - width: auto; - height: 4vw; - margin: 0 10px; - } - .container { - padding-top: 80px; - display: flex; - flex-wrap: wrap; - justify-content: center; - perspective: 1000px; - } - .section { - display: flex; - flex-wrap: wrap; - justify-content: center; - width: 80%; - margin: 20px auto; - background-color: rgba(0, 0, 0, 0.2); - border-radius: 10px; - padding: 20px; - box-shadow: 0px 5px 15px rgba(255, 0, 0, 0.3); - transform-style: preserve-3d; - } - .section-title { - font-size: 2em; - margin-bottom: 10px; - width: 100%; - text-align: center; - font-family: 'Orbitron', sans-serif; - color: #ff6666; - text-shadow: 0px 2px 5px rgba(255, 0, 0, 0.5); - } - .item { - background-color: #220000; - color: #ffcccc; - border-radius: 10px; - padding: 20px; - margin: 10px; - box-shadow: 0px 2px 5px rgba(255, 0, 0, 0.2); - transition: transform 0.2s, box-shadow 0.2s; - width: 200px; - height: 200px; + padding: 1rem var(--spacing-container); display: flex; + justify-content: space-between; align-items: center; - justify-content: center; + z-index: 100; + background: var(--bg-glass); + backdrop-filter: blur(12px); + border-bottom: 1px solid rgba(255, 255, 255, 0.05); +} + +.brand { + font-family: var(--font-display); + font-size: 1.2rem; + color: var(--accent-primary); + font-weight: 700; + letter-spacing: 1px; +} + +.nav-links { + display: flex; + gap: 2rem; +} + +.nav-links a { + font-size: 0.95rem; + font-weight: 500; +} + +.nav-links a:hover, +.nav-links a.active { + color: var(--accent-primary); +} + +/* --- MOBILE FIXES --- */ +@media (max-width: 768px) { + .navbar { + flex-direction: column; + gap: 1rem; + padding: 1rem; + } + + .nav-links { + gap: 1.5rem; + font-size: 0.9rem; + } + + .hero h1 { + font-size: 2.5rem !important; + } + + .tutorial-grid { + grid-template-columns: 1fr !important; + } +} + +/* --- LAYOUT UTILS --- */ +.container { + padding: 0 var(--spacing-container); + max-width: 1000px; + /* Etwas schmaler für bessere Lesbarkeit */ + margin: 0 auto; +} + +.section { + margin: 6rem 0; +} + +.btn { + display: inline-block; + padding: 10px 20px; + border: 1px solid var(--accent-primary); + background: rgba(255, 77, 77, 0.05); + border-radius: var(--radius); + color: var(--accent-primary); + font-family: var(--font-code); + font-weight: 600; + cursor: pointer; + margin-top: 1rem; +} + +.btn:hover { + background: var(--accent-primary); + color: #000; + box-shadow: 0 0 15px var(--accent-glow); +} + +/* --- CONTRIBUTION SECTION --- */ +.contribution-grid { + display: grid; + grid-template-columns: 1fr; + gap: 2.5rem; + margin-top: 2rem; +} + +.contribution-card { + background: linear-gradient(145deg, rgba(255, 255, 255, 0.03) 0%, rgba(255, 255, 255, 0.01) 100%); + border: 1px solid rgba(255, 255, 255, 0.08); + border-radius: calc(var(--radius) * 1.5); + padding: 2.5rem; position: relative; - } - .item-content { - position: relative; - width: 100%; - height: 100%; - padding: 10px; /* Spacing around the content */ - } - - .item-content img { + overflow: hidden; + transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275); +} + +.contribution-card::before { + content: ''; position: absolute; top: 0; left: 0; - width: 100%; - margin: auto; - height: auto; - max-height: calc(100% - 0px); /* Adjust image height here */ - top: 5px; - object-fit: cover; /* Cover the area and maintain aspect ratio */ - border-radius: 10px; /* Round all corners of the image */ - } - - .item-text { - position: absolute; - bottom: 10px; /* Space from the bottom */ - left: 0; + width: 4px; + height: 100%; + background: var(--accent-primary); + opacity: 0.5; +} + +.contribution-card:hover { + transform: scale(1.01); + border-color: rgba(255, 77, 77, 0.3); + box-shadow: 0 20px 40px rgba(0, 0, 0, 0.4); +} + +.contribution-header { + display: flex; + align-items: center; + gap: 1.5rem; + margin-bottom: 2rem; +} + +.contribution-logo { + width: 64px; + height: 64px; + border-radius: 16px; + object-fit: cover; + background: #1a1a1a; + padding: 8px; + border: 1px solid rgba(255, 255, 255, 0.1); + box-shadow: 0 8px 16px rgba(0, 0, 0, 0.3); +} + +.contribution-title-area h3 { + font-family: var(--font-display); + font-size: 1.5rem; + color: #fff; + margin-bottom: 0.5rem; + margin-top: 0; +} + +.contribution-links { + display: flex; + gap: 1rem; +} + +.contribution-link { + font-family: var(--font-code); + font-size: 0.8rem !important; + color: var(--text-muted) !important; + display: flex; + align-items: center; + gap: 0.5rem; + background: rgba(255, 255, 255, 0.05); + padding: 4px 10px; + border-radius: 6px; + transition: 0.2s; + border-bottom: none !important; +} + +.contribution-link:hover { + color: #fff !important; + background: rgba(255, 255, 255, 0.1); +} + +.contribution-link img { + width: 14px; + height: 14px; + filter: brightness(0.8); + display: inline-block; + vertical-align: middle; +} + +.contribution-body p { + font-size: 1.05rem; + color: var(--text-main); + line-height: 1.7; + margin-bottom: 1.5rem; + max-width: 700px; +} + +.contribution-points { + list-style: none; + padding-left: 0; + display: grid; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: 1rem; + margin-bottom: 2rem; +} + +.contribution-points li { + font-size: 0.9rem; + color: var(--text-muted); + display: flex; + align-items: flex-start; + gap: 0.75rem; + margin-bottom: 0; +} + +.contribution-points li::before { + content: '→'; + color: var(--accent-primary); + font-weight: bold; +} + +.status-tag { + display: inline-block; + font-family: var(--font-code); + font-size: 0.7rem; + text-transform: uppercase; + letter-spacing: 1px; + color: var(--accent-primary); + background: rgba(255, 77, 77, 0.1); + padding: 4px 12px; + border-radius: 20px; + border: 1px solid rgba(255, 77, 77, 0.2); +} + +/* --- HERO SECTION --- */ +.hero { + min-height: 60vh; + display: flex; + flex-direction: column; + justify-content: center; + align-items: flex-start; +} + +.hero h1 { + font-size: clamp(3rem, 6vw, 5rem); + line-height: 1.1; + font-weight: 800; + margin-bottom: 1rem; + color: #fff; +} + +.hero p { + max-width: 600px; + font-size: 1.15rem; + color: var(--text-muted); + margin-bottom: 2rem; +} + +/* Social Icons im Hero */ +.social-row { + display: flex; + gap: 1.5rem; + margin-bottom: 2rem; +} + +.social-link img { + width: 32px; + height: 32px; + filter: grayscale(100%); + transition: 0.3s; +} + +.social-link:hover img { + filter: grayscale(0%); + transform: scale(1.1); +} + +/* SKILLS (Integrated) */ +.tech-stack { + display: flex; + gap: 1rem; + flex-wrap: wrap; + margin-top: 1rem; +} + +.tech-tag { + background: rgba(255, 255, 255, 0.05); + padding: 5px 12px; + border-radius: 20px; + font-size: 0.9rem; + font-family: var(--font-code); + border: 1px solid transparent; +} + +.tech-tag:hover { + border-color: var(--accent-primary); +} + +/* --- CARDS & GRID --- */ +.grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); + gap: 2rem; +} + +.card { + background: var(--bg-card); + border: 1px solid rgba(255, 255, 255, 0.05); + border-radius: var(--radius); + overflow: hidden; + transition: transform 0.3s ease; + display: flex; + flex-direction: column; +} + +.card:hover { + transform: translateY(-5px); + border-color: var(--accent-dim); +} + +.card-content { + padding: 1.5rem; + flex-grow: 1; + display: flex; + flex-direction: column; +} + +.card-title { + font-size: 1.2rem; + margin-bottom: 0.5rem; + color: #fff; + font-family: var(--font-display); +} + +.card-desc { + font-size: 0.95rem; + color: var(--text-muted); + margin-bottom: 1rem; +} + +/* Card link styling */ +.card-image-link { + display: block; + text-decoration: none; + color: inherit; +} + +.card-title a { + text-decoration: none; + color: inherit; + transition: color 0.2s; +} + +.card-title a:hover { + color: var(--accent-primary); +} + +/* --- MARKDOWN CONTENT STYLING (Automatisch generiert) --- */ +#markdown-content { + color: var(--text-main); + font-size: 1.1rem; + line-height: 1.8; + max-width: 100%; /* Erlaubt volle Breite auf Mobile */ width: 100%; - text-align: center; - font-size: 1.8em; - } - - .item:hover { - box-shadow: 0px 5px 15px rgba(255, 0, 0, 0.5); - } - .item::before { +} + +@media (min-width: 1025px) { + #markdown-content { + max-width: 800px; /* Begrenzung nur auf Desktop für Lesbarkeit */ + } +} + +/* Headings */ +#markdown-content h1, +#markdown-content h2, +#markdown-content h3 { + color: #fff; + font-family: var(--font-display); + margin-top: 3rem; + margin-bottom: 1.5rem; + letter-spacing: 1px; + position: relative; + scroll-margin-top: 100px; +} + +#markdown-content h1 { + font-size: 2.5rem; + border-bottom: 2px solid var(--accent-primary); + padding-bottom: 1rem; + text-shadow: 0 0 10px var(--accent-dim); +} + +#markdown-content h2 { + font-size: 1.8rem; + border-left: 4px solid var(--accent-primary); + padding-left: 1rem; +} + +/* Standard Text */ +#markdown-content p { + margin-bottom: 1.8rem; + line-height: 1.8; + letter-spacing: 0.3px; +} + +/* Links */ +#markdown-content a { + color: var(--accent-primary); + text-decoration: none; + border-bottom: 1px dotted var(--accent-primary); + transition: all 0.3s ease; +} + +#markdown-content a:hover { + background: var(--accent-dim); + color: #fff; +} + +/* Listen */ +#markdown-content ul, +#markdown-content ol { + margin-left: 2rem; + margin-bottom: 2rem; + color: var(--text-muted); +} + +#markdown-content li { + margin-bottom: 0.8rem; + line-height: 1.7; +} + +#markdown-content ul > li::marker { + color: var(--accent-primary); +} + +/* Inline Code (e.g. `const x`) */ +#markdown-content p code, +#markdown-content li code { + background: rgba(255, 255, 255, 0.1); + padding: 0.2rem 0.4rem; + border-radius: 4px; + font-family: var(--font-code); + font-size: 0.9em; + color: #ff9999; + border: 1px solid rgba(255, 255, 255, 0.05); +} + +/* --- BLOCKQUOTE STYLE --- */ +#markdown-content blockquote { + border-left: 4px solid var(--accent-primary); + background: linear-gradient(90deg, rgba(255, 77, 77, 0.1) 0%, transparent 100%); + margin: 2rem 0; + padding: 1rem 1.5rem; + border-radius: 0 var(--radius) var(--radius) 0; + font-style: italic; + color: #fff; +} + +/* --- TABLE STYLING (Rund & Stylisch) --- */ +#markdown-content table { + width: 100%; + border-collapse: separate; + /* Wichtig für Border-Radius */ + border-spacing: 0; + margin: 2rem 0; + background: var(--bg-panel); + border: 1px solid #333; + border-radius: var(--radius); + overflow: hidden; + /* Damit Ecken rund bleiben */ +} + +#markdown-content th { + background: #222; + color: var(--accent-primary); + font-family: var(--font-display); + font-size: 0.9rem; + text-transform: uppercase; + padding: 1rem; + text-align: left; + border-bottom: 2px solid #333; +} + +#markdown-content td { + padding: 1rem; + border-bottom: 1px solid #2a2a2a; + font-size: 0.95rem; +} + +#markdown-content tr:last-child td { + border-bottom: none; +} + +#markdown-content tr:hover td { + background: rgba(255, 255, 255, 0.02); +} + +.anchor-link { + position: absolute; + left: -1.75rem; /* Etwas weiter weg vom Text */ + top: 50%; + transform: translateY(-50%); + opacity: 0; + transition: opacity 0.2s ease, color 0.2s ease; + text-decoration: none !important; + border: none !important; + background: none !important; + padding-right: 0.5rem; + font-size: 0.8em; + cursor: pointer; + color: var(--accent-primary) !important; +} + +#markdown-content h1:hover .anchor-link, +#markdown-content h2:hover .anchor-link, +#markdown-content h3:hover .anchor-link, +.anchor-link:hover { + opacity: 1; + color: var(--accent-primary) !important; +} + +/* Damit man auf Mobile Geräten den Link sieht (da kein Hover) */ +@media (max-width: 768px) { + .anchor-link { + opacity: 0.5; + left: -1.2rem; + } +} + +/* --- CODE BLOCK STYLING (Mac/Tech Style) --- */ + +/* Der Container um den Code */ +#markdown-content pre { + background: #1e1e1e !important; + /* One Dark Background */ + border-radius: var(--radius); + margin-bottom: 2.5rem; + box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5); + border: 1px solid #333; + position: relative; + padding: 0; + /* Padding wird intern geregelt */ + overflow: hidden; + transition: all 0.3s ease; +} + +#markdown-content pre:hover { + box-shadow: 0 15px 40px rgba(0, 0, 0, 0.7), 0 0 15px rgba(255, 77, 77, 0.1); + border-color: rgba(255, 77, 77, 0.3); +} + +/* Der Header Balken den wir per JS einfügen */ +.code-header { + background: #2a2a2a; + padding: 0.5rem 1rem; + display: flex; + justify-content: space-between; + align-items: center; + border-bottom: 1px solid #333; +} + +.lang-tag { + font-family: var(--font-display); + font-size: 0.75rem; + color: #888; + letter-spacing: 1px; + font-weight: bold; +} + +/* Die 3 Punkte rechts (Deko) */ +.window-controls { + display: flex; + gap: 6px; +} + +.window-controls span { + width: 10px; + height: 10px; + border-radius: 50%; + background-color: #444; +} + +.window-controls span:nth-child(1) { + background: #ff5f56; +} + +.window-controls span:nth-child(2) { + background: #ffbd2e; +} + +.window-controls span:nth-child(3) { + background: #27c93f; +} + +/* Der eigentliche Code Bereich */ +#markdown-content pre code { + display: block; + padding: 1.5rem; + overflow-x: auto; + font-family: var(--font-code); + font-size: 0.95rem; + line-height: 1.6; + background: transparent; + /* Verhindert doppelte Hintergründe */ + color: #abb2bf; + /* Standard Textfarbe One Dark */ +} + +/* Bilder responsive machen */ +#markdown-content img { + max-width: 100%; + height: auto; + border-radius: var(--radius); + border: 1px solid #333; + margin: 1.5rem 0; +} + +/* --- HERO AVATAR --- */ +.hero-content { + display: flex; + align-items: center; + gap: 3rem; + /* Abstand zwischen Text und Bild */ +} + +.avatar-wrapper { + position: relative; + flex-shrink: 0; + /* Verhindert, dass das Bild gequetscht wird */ +} + +.avatar { + width: 180px; + height: 180px; + border-radius: 50%; + object-fit: cover; + border: 2px solid var(--accent-primary); + /* Der Glow-Effekt */ + box-shadow: 0 0 30px rgba(255, 77, 77, 0.2); + transition: transform 0.3s ease, box-shadow 0.3s ease; +} + +.avatar:hover { + transform: scale(1.05) rotate(3deg); + box-shadow: 0 0 50px rgba(255, 77, 77, 0.5); +} + +@media (max-width: 900px) { + .hero-content { + flex-direction: column-reverse; + /* Bild oben, Text unten auf Handy */ + align-items: flex-start; + gap: 2rem; + } + + .avatar { + width: 120px; + height: 120px; + } +} + +.container-docs { + width: 100%; + max-width: 1100px; /* Reduziert für kompakte Zentrierung */ + margin: 0 auto; + padding: 0 1.5rem; +} + +.docs-layout { + display: grid; + grid-template-columns: 200px 1fr; /* Sidebar schmaler (200px) */ + gap: 2.5rem; + align-items: start; + transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1); +} + +.docs-layout.sidebar-collapsed { + grid-template-columns: 0px 1fr; + gap: 0; +} + +.docs-sidebar { + position: sticky; + top: 100px; + height: calc(100vh - 120px); + overflow-y: auto; + border-right: 1px solid rgba(255, 255, 255, 0.05); + padding-right: 1rem; + transition: transform 0.4s, opacity 0.3s, padding 0.4s; +} + +.docs-layout.sidebar-collapsed .docs-sidebar { + transform: translateX(-20px); + opacity: 0; + pointer-events: none; + padding: 0; +} + +/* --- MOBILE NAVIGATION (SIDEBAR) --- */ +.mobile-menu-btn { + display: none; + position: fixed; + bottom: 2rem; + right: 2rem; + width: 56px; + height: 56px; + border-radius: 50%; + background: var(--accent-primary); + border: none; + box-shadow: 0 4px 15px rgba(255, 77, 77, 0.4); + z-index: 1001; + cursor: pointer; + align-items: center; + justify-content: center; +} + +.mobile-menu-btn span { + display: block; + width: 24px; + height: 2px; + background: #000; + position: relative; + transition: 0.3s; +} + +.mobile-menu-btn span::before, +.mobile-menu-btn span::after { content: ''; position: absolute; - top: -1px; - left: -1px; - right: -1px; - bottom: -1px; - border: 1px solid #ff6666; - border-radius: 11px; + width: 24px; + height: 2px; + background: #000; + left: 0; + transition: 0.3s; +} + +.mobile-menu-btn span::before { top: -8px; } +.mobile-menu-btn span::after { top: 8px; } + +.mobile-menu-btn.active span { background: transparent; } +.mobile-menu-btn.active span::before { transform: rotate(45deg); top: 0; } +.mobile-menu-btn.active span::after { transform: rotate(-45deg); top: 0; } + +@media (max-width: 1024px) { + .docs-layout { + grid-template-columns: 1fr; + gap: 0; + } + + .docs-sidebar { + display: block; + position: fixed; + top: 0; + left: -100%; + width: 85%; + max-width: 320px; + height: 100vh; + background: #0d0707; + z-index: 1000; + padding: 5rem 1.5rem 2rem 1.5rem; + transition: 0.4s cubic-bezier(0.4, 0, 0.2, 1); + border-right: 1px solid var(--accent-dim); + } + + .docs-sidebar.active { + left: 0; + box-shadow: 20px 0 60px rgba(0,0,0,0.9); + } + + .mobile-menu-btn { + display: flex; + } + + .sidebar-toggle-desktop, .sidebar-collapsed-trigger { + display: none !important; + } + + .sidebar-overlay { + display: none; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0,0,0,0.8); + backdrop-filter: blur(4px); + z-index: 999; + } + .sidebar-overlay.active { display: block; } +} + +/* Der Content-Bereich (Lesbarkeit behalten) */ +#markdown-content { + max-width: 900px; /* Verhindert, dass Zeilen auf Riesen-Monitoren zu lang werden */ +} + +/* Tab System */ +.tab-wrapper { + margin: 2.5rem 0; + background: #1e1e1e; + border: 1px solid #333; + border-radius: var(--radius); + overflow: hidden; + box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3); +} + +.tab-header-row { + display: flex; + background: #2a2a2a; + border-bottom: 1px solid #333; + padding-left: 0.5rem; +} + +.tab-trigger { + background: transparent; + border: none; + padding: 12px 20px; + color: #888; + cursor: pointer; + font-family: var(--font-code); + font-size: 0.85rem; + transition: 0.3s; + border-bottom: 2px solid transparent; +} + +.tab-trigger.active { + color: var(--accent-primary); + background: #1e1e1e; + border-bottom-color: var(--accent-primary); +} + +.tab-panel { + display: none; +} + +.tab-panel.active { + display: block; +} + +.tab-panel pre { + margin: 0 !important; + border: none !important; + border-radius: 0 !important; +} + +/* Copy Button */ +.copy-btn { + background: rgba(255, 255, 255, 0.05); + border: 1px solid rgba(255, 255, 255, 0.1); + color: #888; + padding: 4px 10px; + border-radius: 4px; + font-size: 0.7rem; + cursor: pointer; + display: flex; + align-items: center; + gap: 6px; + transition: 0.2s; +} + +.copy-btn:hover { + color: #fff; + background: rgba(255, 255, 255, 0.15); +} + +/* Konsolidierung auf 1024px Breakpoint */ +/* --- DOCUMENTATION SIDEBAR --- */ +.sidebar-link { + display: block; + padding: 0.6rem 0; + font-size: 0.9rem; + color: var(--text-muted); + transition: 0.2s; +} + +.sidebar-link:hover { + color: #fff; + padding-left: 5px; +} + +.sidebar-link.active { + color: var(--accent-primary); + font-weight: 600; +} + +.sidebar-category { + margin-top: 1.5rem; + margin-bottom: 0.5rem; + padding: 0.5rem 0; + font-family: var(--font-code); + font-size: 0.75rem; + text-transform: uppercase; + letter-spacing: 0.15em; + color: #999; /* Heller gemacht */ + cursor: pointer; + display: flex; + align-items: center; + gap: 8px; + user-select: none; +} + +.sidebar-category::before { + content: '▼'; + font-size: 0.6rem; + transition: transform 0.3s; +} + +.sidebar-category.collapsed::before { + transform: rotate(-90deg); +} + +.sidebar-child-group { + overflow: hidden; + transition: max-height 0.3s ease-out, opacity 0.3s; +} + +.sidebar-child-group.collapsed { + max-height: 0; opacity: 0; - transition: opacity 0.2s; - } - .item:hover::before { - opacity: 1; - } - @media (max-width: 800px) { - .item { - width: calc(50% - 20px); - height: calc(50vw - 20px); + pointer-events: none; +} + +/* --- DOC PAGINATION (Kleiner gemacht) --- */ +.doc-pagination { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 1rem; + margin-top: 3rem; + padding-top: 2rem; + border-top: 1px solid rgba(255, 255, 255, 0.05); +} + +.nav-page-btn { + display: flex; + flex-direction: column; + padding: 0.75rem 1rem; /* Deutlich kompakter */ + background: rgba(255, 255, 255, 0.02); + border: 1px solid rgba(255, 255, 255, 0.05); + border-radius: 8px; + transition: 0.2s ease; +} + +.nav-page-btn.prev { align-items: flex-start; text-align: left; } +.nav-page-btn.next { align-items: flex-end; text-align: right; } + +.nav-page-btn:hover { + background: rgba(255, 255, 255, 0.04); + border-color: var(--accent-primary); +} + +.nav-label { + font-family: var(--font-code); + font-size: 0.65rem; + text-transform: uppercase; + color: #555; + margin-bottom: 0.2rem; +} + +.nav-title { + font-family: var(--font-body); /* Body font wirkt bei kleineren Buttons sauberer */ + font-size: 0.85rem; + color: var(--text-muted); + font-weight: 500; +} + +.nav-page-btn:hover .nav-title { + color: #fff; +} + +.sidebar-group h4 { + font-family: var(--font-display); + font-size: 0.85rem; + color: #fff; + margin-bottom: 1.5rem; + text-transform: uppercase; + letter-spacing: 2px; +} + +@media (max-width: 600px) { + .doc-pagination { + grid-template-columns: 1fr; } - .section { - width: 100%; - } - .contact-icons img { - width: auto; - height: 10vw; - margin: 0 10px; - } - } - .devvlog-item { - height: 200px; - } - .anleitung-item { - height: 300px; - } \ No newline at end of file +} + +/* Sidebar Toggle Desktop */ +.sidebar-toggle-desktop { + background: rgba(255,255,255,0.03); + border: 1px solid rgba(255,255,255,0.05); + color: #666; + padding: 6px 12px; + border-radius: 6px; + font-family: var(--font-code); + font-size: 0.7rem; + cursor: pointer; + margin-bottom: 2rem; + display: flex; + align-items: center; + gap: 8px; + transition: 0.2s; + width: fit-content; +} + +.sidebar-toggle-desktop:hover { + color: var(--accent-primary); + border-color: var(--accent-dim); +} + +.sidebar-collapsed-trigger { + position: fixed; + left: 1rem; + top: 100px; + z-index: 90; + display: none; + background: var(--bg-card); + border: 1px solid var(--accent-dim); + width: 32px; + height: 32px; + border-radius: 4px; + color: var(--accent-primary); + align-items: center; + justify-content: center; + cursor: pointer; +} + +.docs-layout.sidebar-collapsed .sidebar-collapsed-trigger { + display: flex; +} + + diff --git a/tutorials.html b/tutorials.html new file mode 100644 index 0000000..82093b1 --- /dev/null +++ b/tutorials.html @@ -0,0 +1,438 @@ + + + + + + + BR0TCRAFT | Dev Notes + + + + + + + + + + + +
    +
    +

    Dev Notes

    +

    + Notizen, Quick Tips und längere Artikel. +

    + +
    + +
    + +
    + + +
    + +
    +
    + +
    +
    +
    +
    #01
    +

    std::string vs. std::string_view

    +
    + C++ +
    +

    PROBLEM  Unnötige Kopien beim Übergeben von Strings als + Funktionsparameter.

    +

    LÖSUNG  Wenn der String nur gelesen wird, + std::string_view statt const std::string& nutzen. Kein Ownership, kein + Copy-Overhead. +

    +
    void log(std::string_view msg) {
    +    std::cout << msg;
    +}
    +
    + +
    +
    +
    +
    #02
    +

    dict.get() statt dict[]

    +
    + Python +
    +

    PROBLEM  KeyError beim Zugriff auf einen Key, der + nicht existieren könnte.

    +

    LÖSUNG  .get() gibt None (oder einen + Default) zurück, statt zu crashen.

    +
    val = data.get("key", "default")
    +
    + +
    +
    +
    +
    #03
    +

    Falschen Commit-Message fixen

    +
    + Git +
    +

    PROBLEM  Commit abgeschickt, Message falsch.

    +

    LÖSUNG  --amend vor dem Push. Danach wird's + komplizierter.

    +
    git commit --amend -m "Richtige Message"
    +
    + +
    +
    +
    +
    #04
    +

    Vergessenes virtual im Destruktor

    +
    + C++ +
    +

    PROBLEM  Basisklassen-Destruktor ist nicht virtual + → Derived-Objekte werden nicht korrekt zerstört.

    +

    LÖSUNG  Sobald eine Klasse als Basisklasse gedacht ist:

    +
    class Base {
    +public:
    +    virtual ~Base() = default;
    +};
    +
    + +
    +
    +
    +
    #05
    +

    Optional Chaining statt verschachteltem if

    +
    + JavaScript +
    +

    PROBLEM  Tief verschachtelte Objekte ohne Garantie, dass jede + Ebene existiert.

    +

    LÖSUNG  ?. und ?? kombinieren. + Kürzer, kein TypeError, kein 3-stöckiges if.

    +
    const city = user?.address?.city ?? "Unbekannt";
    +
    + +
    + +
    +
    + +
    +
    + #Nintendo3DS +

    Introduction to 3DS Homebrew Development

    +

    How to start programming homebrew for the Nintendo 3DS.

    +
    + homebrew + 3DS + C++ +
    + Read + More +
    +
    + +
    +
    + #Backend +

    Account-Verwaltung

    +

    Accounts handhaben, ohne Angst zu haben, Passwörter zu leaken.

    +
    + Python + Security + Webserver +
    + Lesen +
    +
    + +
    +
    + #Backend +

    Email-Authentifizierung

    +

    Emails im Backend verschicken, Benutzer über Login-Code verifizieren.

    +
    + Python + Backends + Webserver +
    + Lesen +
    +
    + +
    +
    + #Backend +

    Passwörter hashen & salzen

    +

    Auswirkung eines Datenbankleaks minimieren, so geht's richtig.

    +
    + Python + Security + Backends +
    + Lesen +
    +
    + +
    +
    + #Backend +

    Datenbanken

    +

    Daten, Benutzer und Accounts in einer Datenbank speichern.

    +
    + Python + Backends + Webserver +
    + Lesen +
    +
    + +
    +
    + +
    +
    + + + + + + \ No newline at end of file