1
0

newWebsite2.0

This commit is contained in:
Br0tcraft
2026-08-20 20:36:47 +02:00
parent 7020f970d8
commit 7d975feae4
100 changed files with 6774 additions and 3327 deletions

View File

@@ -1,374 +0,0 @@
<!DOCTYPE html>
<html lang="de">
<head>
<link rel="icon" href="/img/favicon.jpg" type="image/png">
<meta charset="UTF-8">
<title>BR0TCRAFT</title>
<style>
@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: #ffeded;
margin: 0;
overflow-x: hidden;
}
.header {
background-color: #330000;
padding: 20px;
text-align: center;
border-bottom: 5px solid #660000;
position: fixed;
top: 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);
}
h2 {
text-align: center;
margin-bottom: 20px;
}
.container {
display: flex;
flex-direction: column;
align-items: center; /* Zentriert alle Elemente innerhalb von .container */
}
.section {
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: left;
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: left;
font-family: 'Orbitron', sans-serif;
color: #ffffff;
text-shadow: 0px 2px 5px rgba(255, 0, 0, 0.5);
}
.content {
display: flex;
align-items: center;
width: 100%;
justify-content: space-between;
}
.buttons {
display: flex;
flex-direction: column;
justify-content: center;
align-items: flex-start;
margin-left: 10px;
width: calc(100% - 0px);
height: 200px;
}
.buttonsklein {
display: flex;
flex-direction: column;
justify-content: center;
align-items: flex-start;
margin-left: 46%;
width: 30%;
}
.button {
background-color: #660000;
color: #ffffff;
padding: 10px 0px;
border: none;
border-radius: 0px;
margin-bottom: 0px;
font-family: 'Roboto', sans-serif;
font-size: 1.2em;
text-align: center;
text-decoration: none;
box-shadow: 0px 2px 5px rgba(255, 0, 0, 0.2);
cursor: pointer;
transition: background-color 0.2s, box-shadow 0.2s;
width: 100%;
}
.buttonklein {
background-color: #660000;
color: #ffffff;
padding: 10px 0px;
border: none;
border-radius: 0px;
margin-bottom: 0px;
font-family: 'Roboto', sans-serif;
font-size: 1.2em;
text-align: center;
text-decoration: none;
box-shadow: 0px 2px 5px rgba(255, 0, 0, 0.2);
cursor: pointer;
transition: background-color 0.2s, box-shadow 0.2s;
width: 30%;
}
.button:hover {
background-color: #ff6666;
box-shadow: 0px 4px 10px rgba(255, 0, 0, 0.4);
}
.buttonklein:hover {
background-color: #ff6666;
box-shadow: 0px 4px 10px rgba(255, 0, 0, 0.4);
}
.description {
margin-top: 20px; /* Space from the carousel */
padding: 15px;
background-color: rgba(0, 0, 0, 0.3); /* Semi-transparent background */
border-radius: 10px;
border: 2px solid #660000; /* Red border */
}
.description p {
color: #ffffff; /* Light red text color */
font-size: 1.1em;
line-height: 1.6;
}
@media (max-width: 800px) {
.content {
flex-direction: column;
align-items: center;
}
.buttons {
width: 100%;
margin-left: 0;
height: auto;
}
}
.carousel-container {
width: 100%;
display: flex;
justify-content: center;
padding: 20px;
perspective: 1200px;
position: relative;
height: 250px;
overflow: hidden;
margin-top: 20px;
}
.carousel {
display: flex;
transition: transform 0.5s ease;
transform-style: preserve-3d;
width: 100%;
height: 100%;
position: relative;
}
.carousel-item {
width: 250px;
height: 100%;
box-sizing: border-box;
transition: transform 0.5s ease, opacity 0.5s ease;
position: absolute;
top: 0;
left: 50%;
opacity: 1;
}
.carousel-item:nth-child(1) {
transform: translateX(-150%) translateY(20px) scale(0.8) rotateY(-30deg);
}
.carousel-item:nth-child(2) {
transform: translateX(-50%);
}
.carousel-item:nth-child(3) {
transform: translateX(50%) translateY(20px) scale(0.8) rotateY(30deg);
}
h5 {
font-size: 1.7em;
justify-items: left;
}
.carousel-item img {
width: 100%;
height: 100%;
border-radius: 10px;
display: block;
object-fit: cover;
}
.carousel-controls {
display: flex;
justify-content: space-between;
position: absolute;
top: 50%;
left: 0;
transform: translateY(-50%);
width: 100%;
padding: 0; /* Remove padding */
}
.carousel-button {
background-color: #660000;
color: #ffffff;
border: none;
border-radius: 50%;
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.5em;
cursor: pointer;
transition: background-color 0.2s, box-shadow 0.2s;
margin: 0 10px; /* Add margin */
}
.carousel-button:hover {
background-color: #ff6666;
box-shadow: 0px 4px 10px rgba(255, 0, 0, 0.4);
}
</style>
</head>
<body>
<div class="header">
<a href="/" style="text-decoration: none; color: inherit;">
<h1>BR0TCRAFT</h1>
</a>
</div>
<div class="container">
<h2>Gamename</h2>
<div class="section">
<p>Datum</p>
<div class="section-title">Update: 12.10.2024</div>
<p>ein Button zum Interaktieren</p>
<div class="content">
<div class="buttons">
<a href="/play/Tiny-Snake/snake.html" class="button">Spielen</a>
</div>
</div>
<p>ein kleiner Button zum Interaktieren</p>
<div class="content">
<div class="buttonsklein">
<a href="/play/Tiny-Snake/snake.html" class="buttonklein">Spielen</a>
</div>
</div>
<h5>Unter Überschrift:</h5>
<div class="description">
<p>Eine simple zwischennotiz</p>
</div>
<p>Karusell zur Bilder implementierung</p>
<div class="carousel-container">
<div class="carousel">
<div class="carousel-item">
<img src="/img/Apps/Snake/snake1.PNG" alt="App 1 Image 1">
</div>
<div class="carousel-item">
<img src="/img/Apps/snake.jpg" alt="App 1 Image 2">
</div>
<div class="carousel-item">
<img src="/img/Apps/Snake/Snake2.png" alt="App 1 Image 3">
</div>
</div>
<div class="carousel-controls">
<button class="carousel-button prev">&lt;</button>
<button class="carousel-button next">&gt;</button>
</div>
</div>
</div>
<div class=" section">
<p>Noch eine Kategorie für weiteres Datum</p>
</div>
<script>
const carousel = document.querySelector('.carousel');
const items = document.querySelectorAll('.carousel-item');
const prevButton = document.querySelector('.prev');
const nextButton = document.querySelector('.next');
let currentItem = 2; // Start with the middle item
function updateCarousel() {
items.forEach((item, index) => {
if (index === currentItem - 1) {
// Center item
item.style.transform = 'translateX(-50%)';
} else if (index < currentItem - 1) {
// Items to the left
item.style.transform = `translateX(${-150 - (currentItem - index - 2) * 100}%) translateY(20px) scale(0.8) rotateY(-30deg)`;
} else {
// Items to the right
item.style.transform = `translateX(${50 + (index - currentItem) * 100}%) translateY(20px) scale(0.8) rotateY(30deg)`;
}
});
}
prevButton.addEventListener('click', () => {
currentItem--;
if (currentItem < 1) {
currentItem = items.length;
}
updateCarousel();
});
nextButton.addEventListener('click', () => {
currentItem++;
if (currentItem > items.length) {
currentItem = 1;
}
updateCarousel();
});
// Initial setup
updateCarousel();
</script>
</body>
</html>

View File

@@ -1,57 +0,0 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Br0tcraft</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div class="header-container">
</div>
<div class="Background-Leiste"></div>
<div class="Leiste"></div>
<div class="container">
<a class="menu-link-home" href="">BR0TCRAFT</a>
<div class="mobile-menu-icon" onclick="toggleMenu()">
<div class="hamburger-icon"></div>
<div class="hamburger-icon"></div>
<div class="hamburger-icon"></div>
</div>
<div class="menu-links">
<a class="menu-link" href="">Spiele</a>
<a class="menu-link" href="">Tutorials</a>
<a class="menu-link" href="">Extensions</a>
<a class="menu-link" href="">Kontakt</a>
<a class="menu-link" href="">funny-Seiten</a>
</div>
</div>
<div class="mobile-menu" onclick="toggleMenu()">
<a class="menu-link-home" href="">BR0TCRAFT</a>
<br><br><br>
<a class="menu-link-mobile" href="">Spiele</a>
<a class="menu-link-mobile" href="">Tutorials</a>
<a class="menu-link-mobile" href="">Turbowarp-Extensions</a>
<a class="menu-link-mobile" href="">Kontakt</a>
<a class="menu-link-mobile" href="">funny-Seiten</a>
</div>
<h1 class="headline">Example</h1>
Dies wird genutzt für weitere Websiten als Template
<script>
function toggleMenu() {
var menuLinks = document.querySelector(".menu-links");
var mobileMenu = document.querySelector(".mobile-menu");
var body = document.body;
menuLinks.classList.toggle("show");
mobileMenu.classList.toggle("show");
}
</script>
</body>
</html>

View File

@@ -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);
}
}

View File

@@ -1 +0,0 @@
A simple HTML/JS/CSS starter template

309
about.html Normal file
View File

@@ -0,0 +1,309 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>BR0TCRAFT | About</title>
<link rel="stylesheet" href="style.css">
<link
href="https://fonts.googleapis.com/css2?family=Fira+Code:wght@400;600&family=Inter:wght@300;400;600;800&family=Orbitron:wght@700&display=swap"
rel="stylesheet">
<style>
.about-prose {
max-width: 680px;
color: var(--text-muted);
line-height: 1.8;
font-size: 1rem;
}
.about-prose p {
margin-bottom: 1rem;
}
.about-prose strong {
color: #fff;
font-weight: 600;
}
.trait-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
gap: 1rem;
margin-top: 1.25rem;
}
.trait-box {
border: 1px solid #222;
border-radius: var(--radius);
padding: 1.25rem 1.5rem;
}
.trait-box .trait-label {
font-family: var(--font-code);
font-size: 0.72rem;
color: var(--accent-primary);
letter-spacing: 0.08em;
text-transform: uppercase;
margin-bottom: 0.6rem;
}
.trait-box ul {
list-style: none;
padding: 0;
margin: 0;
}
.trait-box ul li {
color: var(--text-muted);
font-size: 0.9rem;
line-height: 1.7;
padding-left: 1rem;
position: relative;
}
.trait-box ul li::before {
content: '';
position: absolute;
left: 0;
color: #444;
}
/* Journey Timeline */
.timeline {
position: relative;
max-width: 680px;
margin-top: 1.5rem;
}
.timeline::before {
content: '';
position: absolute;
left: 7px;
top: 6px;
bottom: 0;
width: 1px;
background: #2a2a2a;
}
.tl-item {
display: flex;
gap: 1.5rem;
margin-bottom: 2rem;
position: relative;
}
.tl-dot {
width: 15px;
height: 15px;
border-radius: 50%;
background: #1a1a1a;
border: 1px solid #444;
flex-shrink: 0;
margin-top: 4px;
position: relative;
z-index: 1;
transition: border-color 0.2s;
}
.tl-item:hover .tl-dot {
border-color: var(--accent-primary);
}
.tl-label {
font-family: var(--font-code);
font-size: 0.75rem;
color: #555;
margin-bottom: 0.25rem;
}
.tl-body h4 {
color: #fff;
font-size: 0.95rem;
margin-bottom: 0.4rem;
}
.tl-body p {
color: var(--text-muted);
font-size: 0.9rem;
line-height: 1.65;
}
.tl-tag {
display: inline-block;
font-size: 0.72rem;
font-family: var(--font-code);
color: var(--accent-primary);
background: rgba(255, 77, 77, 0.07);
padding: 1px 6px;
border-radius: 3px;
margin-top: 0.4rem;
}
/* Plans */
.plan-list {
max-width: 600px;
margin-top: 1.25rem;
}
.plan-item {
display: flex;
align-items: flex-start;
gap: 1rem;
padding: 1rem 0;
border-bottom: 1px solid #1e1e1e;
}
.plan-item:last-child {
border-bottom: none;
}
.plan-index {
font-family: var(--font-code);
font-size: 0.75rem;
color: #444;
min-width: 28px;
margin-top: 2px;
}
.plan-item h4 {
color: #fff;
font-size: 0.95rem;
margin-bottom: 0.25rem;
}
.plan-item p {
color: var(--text-muted);
font-size: 0.88rem;
line-height: 1.6;
}
.section-divider {
height: 1px;
background: #1e1e1e;
margin: 0;
}
</style>
</head>
<body>
<nav class="navbar">
<div class="brand">BR0TCRAFT</div>
<div class="nav-links">
<a href="index.html">Home</a>
<a href="projects.html">Projects</a>
<a href="tutorials.html">Dev Notes</a>
<a href="about.html" class="active">About</a>
<a href="assets.html">Assets</a>
</div>
</nav>
<div class="container">
<section class="section">
<h1 style="font-family: var(--font-display); margin-bottom: 2rem; color: #fff;">About me</h1>
<div class="about-prose">
<p>
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.
<br><br>
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.
<br><br>
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.
</p>
</div>
<div class="section-divider"></div>
<section class="section">
<h2 style="font-family: var(--font-display); margin-bottom: 0.5rem; color: #fff;">Journey</h2>
<p style="color: var(--text-muted); font-size: 0.9rem; margin-bottom: 2rem;">Was ich bisher so ausprobiert hatte
</p>
<div class="timeline">
<div class="tl-item">
<div class="tl-dot"></div>
<div class="tl-body">
<div class="tl-label">Anfang</div>
<h4>Scratch</h4>
<p>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.</p>
<span class="tl-tag">Scratch</span>
</div>
</div>
<div class="tl-item">
<div class="tl-dot"></div>
<div class="tl-body">
<div class="tl-label">Erstes echtes Projekt</div>
<h4>Unity &amp; C#</h4>
<p>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.</p>
<span class="tl-tag">C#</span>
<span class="tl-tag">Unity</span>
</div>
</div>
<div class="tl-item">
<div class="tl-dot"></div>
<div class="tl-body">
<div class="tl-label">Alternative</div>
<h4>Godot ausprobiert</h4>
<p>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. </p>
<span class="tl-tag">GDScript</span>
<span class="tl-tag">Godot</span>
</div>
</div>
<div class="tl-item">
<div class="tl-dot"></div>
<div class="tl-body">
<div class="tl-label">Parallel</div>
<h4>Python &amp; JavaScript</h4>
<p>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.
</p>
<span class="tl-tag">Python</span>
<span class="tl-tag">JavaScript</span>
</div>
</div>
<div class="tl-item">
<div class="tl-dot" style="border-color: var(--accent-primary);"></div>
<div class="tl-body">
<div class="tl-label">Aktuell</div>
<h4>C++</h4>
<p>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.</p>
<span class="tl-tag">C++</span>
</div>
</div>
</div>
</section>
</div>
</body>
</html>

View File

@@ -1,346 +0,0 @@
<!DOCTYPE html>
<html lang="de">
<head>
<link rel="icon" href="/img/favicon.jpg" type="image/png">
<meta charset="UTF-8">
<title>BR0TCRAFT</title>
<style>
@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;
margin: 0;
overflow-x: hidden;
}
.header {
background-color: #330000;
padding: 20px;
text-align: center;
border-bottom: 5px solid #660000;
position: fixed;
top: 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);
}
.container {
padding-top: 80px;
display: flex;
flex-wrap: wrap;
justify-content: center;
perspective: 1000px;
}
.section {
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: 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: left;
font-family: 'Orbitron', sans-serif;
color: #ffffff;
text-shadow: 0px 2px 5px rgba(255, 0, 0, 0.5);
}
.content {
display: flex;
align-items: center;
width: 100%;
justify-content: space-between;
}
.item {
background-color: #220000;
color: #ffcccc;
border-radius: 10px;
box-shadow: 0px 2px 5px rgba(255, 0, 0, 0.2);
width: 200px;
height: 200px;
display: flex;
align-items: center;
justify-content: center;
position: relative;
}
.item-content {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
padding: 10px;
}
.item-content img {
max-width: 100%;
max-height: 100%;
object-fit: cover;
border-radius: 10px;
}
.buttons {
display: flex;
flex-direction: column;
justify-content: center;
align-items: flex-start;
margin-left: 20px;
width: calc(100% - 220px);
height: 200px;
}
.button {
background-color: #660000;
color: #ffffff;
padding: 10px 0px;
border: none;
border-radius: 5px;
margin-bottom: 10px;
font-family: 'Roboto', sans-serif;
font-size: 1.2em;
text-align: center;
text-decoration: none;
box-shadow: 0px 2px 5px rgba(255, 0, 0, 0.2);
cursor: pointer;
transition: background-color 0.2s, box-shadow 0.2s;
width: 100%;
}
.button:hover {
background-color: #ff6666;
box-shadow: 0px 4px 10px rgba(255, 0, 0, 0.4);
}
.description {
margin-top: 20px; /* Space from the carousel */
padding: 15px;
background-color: rgba(0, 0, 0, 0.3); /* Semi-transparent background */
border-radius: 10px;
border: 2px solid #660000; /* Red border */
}
.description p {
color: #ffcccc; /* Light red text color */
font-size: 1.1em;
line-height: 1.6;
}
@media (max-width: 800px) {
.content {
flex-direction: column;
align-items: center;
}
.item {
width: 100%;
height: auto;
margin-bottom: 20px;
}
.buttons {
width: 100%;
margin-left: 0;
height: auto;
}
}
.carousel-container {
width: 100%;
display: flex;
justify-content: center;
padding: 20px;
perspective: 1200px;
position: relative;
height: 250px;
overflow: hidden;
margin-top: 20px;
}
.carousel {
display: flex;
transition: transform 0.5s ease;
transform-style: preserve-3d;
width: 100%;
height: 100%;
position: relative;
}
.carousel-item {
width: 250px;
height: 100%;
box-sizing: border-box;
transition: transform 0.5s ease, opacity 0.5s ease;
position: absolute;
top: 0;
left: 50%;
opacity: 1;
}
.carousel-item:nth-child(1) {
transform: translateX(-150%) translateY(20px) scale(0.8) rotateY(-30deg);
}
.carousel-item:nth-child(2) {
transform: translateX(-50%);
}
.carousel-item:nth-child(3) {
transform: translateX(50%) translateY(20px) scale(0.8) rotateY(30deg);
}
.carousel-item img {
width: 100%;
height: 100%;
border-radius: 10px;
display: block;
object-fit: cover;
}
.carousel-controls {
display: flex;
justify-content: space-between;
position: absolute;
top: 50%;
left: 0;
transform: translateY(-50%);
width: 100%;
padding: 0; /* Remove padding */
}
.carousel-button {
background-color: #660000;
color: #ffffff;
border: none;
border-radius: 50%;
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.5em;
cursor: pointer;
transition: background-color 0.2s, box-shadow 0.2s;
margin: 0 10px; /* Add margin */
}
.carousel-button:hover {
background-color: #ff6666;
box-shadow: 0px 4px 10px rgba(255, 0, 0, 0.4);
}
</style>
</head>
<body>
<div class="header">
<a href="/" style="text-decoration: none; color: inherit;">
<h1>BR0TCRAFT</h1>
</a>
</div>
<div class="container">
<div class="section">
<div class="section-title">Br0tbot (in Entwicklung, kann noch nicht alles)</div>
<div class="content">
<div class="item">
<div class="item-content">
<img src="/img/Apps/TigerBr0tbot.jpg" alt="App 1 Image">
</div>
</div>
<div class="buttons">
<a href="#" class="button">Spielen</a>
<a href="#" class="button">Herunterladen</a>
<a href="#" class="button">Github-Repository</a>
</div>
</div>
<div class="description">
<p>
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.
</p>
<p>
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.
</p>
<p>
Der Bot ist Open Source und für das Selbst-Hosting gedacht.
</p>
<p>
Ps: Der Bot ist noch in Entwicklung und es stehen evtl noch nicht alle versprochenen funktionen zur Verfügunng
</p>
</div>
<div class="carousel-container">
<div class="carousel">
<div class="carousel-item">
<img src="/img/Apps/Snake/snake1.PNG" alt="App 1 Image 1">
</div>
<div class="carousel-item">
<img src="/img/Apps/TigerBr0tbot.jpg" alt="App 1 Image 2">
</div>
<div class="carousel-item">
<img src="/img/Apps/Snake/Snake2.png" alt="App 1 Image 3">
</div>
</div>
<div class="carousel-controls">
<button class="carousel-button prev">&lt;</button>
<button class="carousel-button next">&gt;</button>
</div>
</div>
</div>
<script>
const carousel = document.querySelector('.carousel');
const items = document.querySelectorAll('.carousel-item');
const prevButton = document.querySelector('.prev');
const nextButton = document.querySelector('.next');
let currentItem = 2; // Start with the middle item
function updateCarousel() {
items.forEach((item, index) => {
if (index === currentItem - 1) {
// Center item
item.style.transform = 'translateX(-50%)';
} else if (index < currentItem - 1) {
// Items to the left
item.style.transform = `translateX(${-150 - (currentItem - index - 2) * 100}%) translateY(20px) scale(0.8) rotateY(-30deg)`;
} else {
// Items to the right
item.style.transform = `translateX(${50 + (index - currentItem) * 100}%) translateY(20px) scale(0.8) rotateY(30deg)`;
}
});
}
prevButton.addEventListener('click', () => {
currentItem--;
if (currentItem < 1) {
currentItem = items.length;
}
updateCarousel();
});
nextButton.addEventListener('click', () => {
currentItem++;
if (currentItem > items.length) {
currentItem = 1;
}
updateCarousel();
});
// Initial setup
updateCarousel();
</script>
</body>
</html>

View File

@@ -1,345 +0,0 @@
<!DOCTYPE html>
<html lang="de">
<head>
<link rel="icon" href="/img/favicon.jpg" type="image/png">
<meta charset="UTF-8">
<title>BR0TCRAFT</title>
<style>
@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;
margin: 0;
overflow-x: hidden;
}
.header {
background-color: #330000;
padding: 20px;
text-align: center;
border-bottom: 5px solid #660000;
position: fixed;
top: 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);
}
.container {
padding-top: 80px;
display: flex;
flex-wrap: wrap;
justify-content: center;
perspective: 1000px;
}
.section {
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: 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: left;
font-family: 'Orbitron', sans-serif;
color: #ffffff;
text-shadow: 0px 2px 5px rgba(255, 0, 0, 0.5);
}
.content {
display: flex;
align-items: center;
width: 100%;
justify-content: space-between;
}
.item {
background-color: #220000;
color: #ffcccc;
border-radius: 10px;
box-shadow: 0px 2px 5px rgba(255, 0, 0, 0.2);
width: 200px;
height: 200px;
display: flex;
align-items: center;
justify-content: center;
position: relative;
}
.item-content {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
padding: 10px;
}
.item-content img {
max-width: 100%;
max-height: 100%;
object-fit: cover;
border-radius: 10px;
}
.buttons {
display: flex;
flex-direction: column;
justify-content: center;
align-items: flex-start;
margin-left: 20px;
width: calc(100% - 220px);
height: 200px;
}
.button {
background-color: #660000;
color: #ffffff;
padding: 10px 0px;
border: none;
border-radius: 5px;
margin-bottom: 10px;
font-family: 'Roboto', sans-serif;
font-size: 1.2em;
text-align: center;
text-decoration: none;
box-shadow: 0px 2px 5px rgba(255, 0, 0, 0.2);
cursor: pointer;
transition: background-color 0.2s, box-shadow 0.2s;
width: 100%;
}
.button:hover {
background-color: #ff6666;
box-shadow: 0px 4px 10px rgba(255, 0, 0, 0.4);
}
.description {
margin-top: 20px; /* Space from the carousel */
padding: 15px;
background-color: rgba(0, 0, 0, 0.3); /* Semi-transparent background */
border-radius: 10px;
border: 2px solid #660000; /* Red border */
}
.description p {
color: #ffcccc; /* Light red text color */
font-size: 1.1em;
line-height: 1.6;
}
@media (max-width: 800px) {
.content {
flex-direction: column;
align-items: center;
}
.item {
width: 100%;
height: auto;
margin-bottom: 20px;
}
.buttons {
width: 100%;
margin-left: 0;
height: auto;
}
}
.carousel-container {
width: 100%;
display: flex;
justify-content: center;
padding: 20px;
perspective: 1200px;
position: relative;
height: 250px;
overflow: hidden;
margin-top: 20px;
}
.carousel {
display: flex;
transition: transform 0.5s ease;
transform-style: preserve-3d;
width: 100%;
height: 100%;
position: relative;
}
.carousel-item {
width: 250px;
height: 100%;
box-sizing: border-box;
transition: transform 0.5s ease, opacity 0.5s ease;
position: absolute;
top: 0;
left: 50%;
opacity: 1;
}
.carousel-item:nth-child(1) {
transform: translateX(-150%) translateY(20px) scale(0.8) rotateY(-30deg);
}
.carousel-item:nth-child(2) {
transform: translateX(-50%);
}
.carousel-item:nth-child(3) {
transform: translateX(50%) translateY(20px) scale(0.8) rotateY(30deg);
}
.carousel-item img {
width: 100%;
height: 100%;
border-radius: 10px;
display: block;
object-fit: cover;
}
.carousel-controls {
display: flex;
justify-content: space-between;
position: absolute;
top: 50%;
left: 0;
transform: translateY(-50%);
width: 100%;
padding: 0; /* Remove padding */
}
.carousel-button {
background-color: #660000;
color: #ffffff;
border: none;
border-radius: 50%;
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.5em;
cursor: pointer;
transition: background-color 0.2s, box-shadow 0.2s;
margin: 0 10px; /* Add margin */
}
.carousel-button:hover {
background-color: #ff6666;
box-shadow: 0px 4px 10px rgba(255, 0, 0, 0.4);
}
</style>
</head>
<body>
<div class="header">
<a href="/" style="text-decoration: none; color: inherit;">
<h1>BR0TCRAFT</h1>
</a>
</div>
<div class="container">
<div class="section">
<div class="section-title">Br0tkabeln (beta)</div>
<div class="content">
<div class="item">
<div class="item-content">
<img src="/img/Apps/Br0tkabeln.jpg" alt="App 1 Image">
</div>
</div>
<div class="buttons">
<a href="https://vocabulary.br0tcraft.de/" class="button">Starten</a>
<a href="#" class="button">Github-Repository</a>
</div>
</div>
<div class="description">
<p>
Ein simpler und hoffentlich auch einfach zu verstehender Vokabeltrainer
</p>
<p>
Der Vokabeltrainer wurde mit Python, Html, Css, Javascript und Flask erstellt. Deine Vokabeln werden kostenlos auf dem Server gespeichert
</p>
<p>
Es werden jediglich Benutzername, Vokabeln(English & Deutsch) und deren Level in Klartext auf dem Server gespeichert. Das Passwort wiederum wird zur Sicherheit gehasht.
</p>
<p>
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
</p>
</div>
<div class="carousel-container">
<div class="carousel">
<div class="carousel-item">
<img src="/img/Apps/Br0tkabeln/Vocabbild.PNG" alt="App 1 Image 1">
</div>
<div class="carousel-item">
<img src="/img/Apps/Br0tkabeln.jpg" alt="App 1 Image 2">
</div>
<div class="carousel-item">
<img src="/img/Apps/Br0tkabeln/Vocabbild2.PNG" alt="App 1 Image 3">
</div>
</div>
<div class="carousel-controls">
<button class="carousel-button prev">&lt;</button>
<button class="carousel-button next">&gt;</button>
</div>
</div>
</div>
<script>
const carousel = document.querySelector('.carousel');
const items = document.querySelectorAll('.carousel-item');
const prevButton = document.querySelector('.prev');
const nextButton = document.querySelector('.next');
let currentItem = 2; // Start with the middle item
function updateCarousel() {
items.forEach((item, index) => {
if (index === currentItem - 1) {
// Center item
item.style.transform = 'translateX(-50%)';
} else if (index < currentItem - 1) {
// Items to the left
item.style.transform = `translateX(${-150 - (currentItem - index - 2) * 100}%) translateY(20px) scale(0.8) rotateY(-30deg)`;
} else {
// Items to the right
item.style.transform = `translateX(${50 + (index - currentItem) * 100}%) translateY(20px) scale(0.8) rotateY(30deg)`;
}
});
}
prevButton.addEventListener('click', () => {
currentItem--;
if (currentItem < 1) {
currentItem = items.length;
}
updateCarousel();
});
nextButton.addEventListener('click', () => {
currentItem++;
if (currentItem > items.length) {
currentItem = 1;
}
updateCarousel();
});
// Initial setup
updateCarousel();
</script>
</body>
</html>

View File

@@ -1,274 +0,0 @@
!DOCTYPE html>
<html lang="de">
<head>
<link rel="icon" href="/img/favicon.jpg" type="image/png">
<meta charset="UTF-8">
<title>BR0TCRAFT</title>
<style>
@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;
margin: 0;
overflow-x: hidden;
}
.header {
background-color: #330000;
padding: 20px;
text-align: center;
border-bottom: 5px solid #660000;
position: fixed;
top: 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);
}
.container {
padding-top: 80px;
display: flex;
flex-wrap: wrap;
justify-content: center;
perspective: 1000px;
}
.section {
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: 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: left;
font-family: 'Orbitron', sans-serif;
color: #ffffff;
text-shadow: 0px 2px 5px rgba(255, 0, 0, 0.5);
}
.content {
display: flex;
align-items: center;
width: 100%;
justify-content: space-between;
}
.item {
background-color: #220000;
color: #ffcccc;
border-radius: 10px;
box-shadow: 0px 2px 5px rgba(255, 0, 0, 0.2);
width: 200px;
height: 200px;
display: flex;
align-items: center;
justify-content: center;
position: relative;
}
.item-content {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
padding: 10px;
}
.item-content img {
max-width: 100%;
max-height: 100%;
object-fit: cover;
border-radius: 10px;
}
.buttons {
display: flex;
flex-direction: column;
justify-content: center;
align-items: flex-start;
margin-left: 20px;
width: calc(100% - 220px);
height: 200px;
}
.button {
background-color: #660000;
color: #ffffff;
padding: 10px 0px;
border: none;
border-radius: 5px;
margin-bottom: 10px;
font-family: 'Roboto', sans-serif;
font-size: 1.2em;
text-align: center;
text-decoration: none;
box-shadow: 0px 2px 5px rgba(255, 0, 0, 0.2);
cursor: pointer;
transition: background-color 0.2s, box-shadow 0.2s;
width: 100%;
}
.button:hover {
background-color: #ff6666;
box-shadow: 0px 4px 10px rgba(255, 0, 0, 0.4);
}
.description {
margin-top: 20px; /* Space from the carousel */
padding: 15px;
background-color: rgba(0, 0, 0, 0.3); /* Semi-transparent background */
border-radius: 10px;
border: 2px solid #660000; /* Red border */
}
.description p {
color: #ffcccc; /* Light red text color */
font-size: 1.1em;
line-height: 1.6;
}
@media (max-width: 800px) {
.content {
flex-direction: column;
align-items: center;
}
.item {
width: 100%;
height: auto;
margin-bottom: 20px;
}
.buttons {
width: 100%;
margin-left: 0;
height: auto;
}
}
.carousel-container {
width: 100%;
display: flex;
justify-content: center;
padding: 20px;
perspective: 1200px;
position: relative;
height: 250px;
overflow: hidden;
margin-top: 20px;
}
.carousel {
display: flex;
transition: transform 0.5s ease;
transform-style: preserve-3d;
width: 100%;
height: 100%;
position: relative;
}
.carousel-item {
width: 250px;
height: 100%;
box-sizing: border-box;
transition: transform 0.5s ease, opacity 0.5s ease;
position: absolute;
top: 0;
left: 50%;
opacity: 1;
}
.carousel-item:nth-child(1) {
transform: translateX(-150%) translateY(20px) scale(0.8) rotateY(-30deg);
}
.carousel-item:nth-child(2) {
transform: translateX(-50%);
}
.carousel-item:nth-child(3) {
transform: translateX(50%) translateY(20px) scale(0.8) rotateY(30deg);
}
.carousel-item img {
width: 100%;
height: 100%;
border-radius: 10px;
display: block;
object-fit: cover;
}
.carousel-controls {
display: flex;
justify-content: space-between;
position: absolute;
top: 50%;
left: 0;
transform: translateY(-50%);
width: 100%;
padding: 0; /* Remove padding */
}
.carousel-button {
background-color: #660000;
color: #ffffff;
border: none;
border-radius: 50%;
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.5em;
cursor: pointer;
transition: background-color 0.2s, box-shadow 0.2s;
margin: 0 10px; /* Add margin */
}
.carousel-button:hover {
background-color: #ff6666;
box-shadow: 0px 4px 10px rgba(255, 0, 0, 0.4);
}
</style>
</head>
<body>
<div class="header">
<a href="/" style="text-decoration: none; color: inherit;">
<h1>BR0TCRAFT</h1>
</a>
</div>
<div class="container">
<div class="section">
<div class="section-title">Sensor data</div>
<div class="content">
<div class="item">
<div class="item-content">
<img src="/img/Apps/PocketCode/Sensor/Sensor.png" alt="App 1 Image">
</div>
</div>
<div class="buttons">
<a href="https://share.catrob.at/app/project/445cff43-cbda-401a-bae7-924ec0dc6384" class="button">Spielen/Herunterladen</a>
</div>
</div>
<div class="description">
<p>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</p>
<p>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)</p>
<p> Spielbar nur am Handy mit der App <a href="/enviroments/PocketCode/index.html">Pocket Code</a></p>
</div>
</div>
</body>
</html>

View File

@@ -1,334 +0,0 @@
<!DOCTYPE html>
<html lang="de">
<head>
<link rel="icon" href="/img/favicon.jpg" type="image/png">
<meta charset="UTF-8">
<title>BR0TCRAFT</title>
<style>
@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;
margin: 0;
overflow-x: hidden;
}
.header {
background-color: #330000;
padding: 20px;
text-align: center;
border-bottom: 5px solid #660000;
position: fixed;
top: 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);
}
.container {
padding-top: 80px;
display: flex;
flex-wrap: wrap;
justify-content: center;
perspective: 1000px;
}
.section {
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: 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: left;
font-family: 'Orbitron', sans-serif;
color: #ffffff;
text-shadow: 0px 2px 5px rgba(255, 0, 0, 0.5);
}
.content {
display: flex;
align-items: center;
width: 100%;
justify-content: space-between;
}
.item {
background-color: #220000;
color: #ffcccc;
border-radius: 10px;
box-shadow: 0px 2px 5px rgba(255, 0, 0, 0.2);
width: 200px;
height: 200px;
display: flex;
align-items: center;
justify-content: center;
position: relative;
}
.item-content {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
padding: 10px;
}
.item-content img {
max-width: 100%;
max-height: 100%;
object-fit: cover;
border-radius: 10px;
}
.buttons {
display: flex;
flex-direction: column;
justify-content: center;
align-items: flex-start;
margin-left: 20px;
width: calc(100% - 220px);
height: 200px;
}
.button {
background-color: #660000;
color: #ffffff;
padding: 10px 0px;
border: none;
border-radius: 5px;
margin-bottom: 10px;
font-family: 'Roboto', sans-serif;
font-size: 1.2em;
text-align: center;
text-decoration: none;
box-shadow: 0px 2px 5px rgba(255, 0, 0, 0.2);
cursor: pointer;
transition: background-color 0.2s, box-shadow 0.2s;
width: 100%;
}
.button:hover {
background-color: #ff6666;
box-shadow: 0px 4px 10px rgba(255, 0, 0, 0.4);
}
.description {
margin-top: 20px; /* Space from the carousel */
padding: 15px;
background-color: rgba(0, 0, 0, 0.3); /* Semi-transparent background */
border-radius: 10px;
border: 2px solid #660000; /* Red border */
}
.description p {
color: #ffcccc; /* Light red text color */
font-size: 1.1em;
line-height: 1.6;
}
@media (max-width: 800px) {
.content {
flex-direction: column;
align-items: center;
}
.item {
width: 100%;
height: auto;
margin-bottom: 20px;
}
.buttons {
width: 100%;
margin-left: 0;
height: auto;
}
}
.carousel-container {
width: 100%;
display: flex;
justify-content: center;
padding: 20px;
perspective: 1200px;
position: relative;
height: 250px;
overflow: hidden;
margin-top: 20px;
}
.carousel {
display: flex;
transition: transform 0.5s ease;
transform-style: preserve-3d;
width: 100%;
height: 100%;
position: relative;
}
.carousel-item {
width: 250px;
height: 100%;
box-sizing: border-box;
transition: transform 0.5s ease, opacity 0.5s ease;
position: absolute;
top: 0;
left: 50%;
opacity: 1;
}
.carousel-item:nth-child(1) {
transform: translateX(-150%) translateY(20px) scale(0.8) rotateY(-30deg);
}
.carousel-item:nth-child(2) {
transform: translateX(-50%);
}
.carousel-item:nth-child(3) {
transform: translateX(50%) translateY(20px) scale(0.8) rotateY(30deg);
}
.carousel-item img {
width: 100%;
height: 100%;
border-radius: 10px;
display: block;
object-fit: cover;
}
.carousel-controls {
display: flex;
justify-content: space-between;
position: absolute;
top: 50%;
left: 0;
transform: translateY(-50%);
width: 100%;
padding: 0; /* Remove padding */
}
.carousel-button {
background-color: #660000;
color: #ffffff;
border: none;
border-radius: 50%;
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.5em;
cursor: pointer;
transition: background-color 0.2s, box-shadow 0.2s;
margin: 0 10px; /* Add margin */
}
.carousel-button:hover {
background-color: #ff6666;
box-shadow: 0px 4px 10px rgba(255, 0, 0, 0.4);
}
</style>
</head>
<body>
<div class="header">
<a href="/" style="text-decoration: none; color: inherit;">
<h1>BR0TCRAFT</h1>
</a>
</div>
<div class="container">
<div class="section">
<div class="section-title">Tower defense (Beta)</div>
<div class="content">
<div class="item">
<div class="item-content">
<img src="/img/Apps/PocketCode/Tower/Tower.png" alt="App 1 Image">
</div>
</div>
<div class="buttons">
<a href="https://share.catrob.at/app/project/ef0b260c-a0a6-4e87-9eab-d7759de1f092" class="button">Spielen/Herunterladen</a>
</div>
</div>
<div class="description">
<p>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 </p>
<p>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</p>
<p> Spielbar nur am Handy mit der App <a href="/enviroments/PocketCode/index.html">Pocket Code</a></p>
</div>
<div class="carousel-container">
<div class="carousel">
<div class="carousel-item">
<img src="/img/Apps/PocketCode/Tower/Tower1.jpg" alt="App 1 Image 1">
</div>
<div class="carousel-item">
<img src="/img/Apps/PocketCode/Tower/Tower.png" alt="App 1 Image 2">
</div>
<div class="carousel-item">
<img src="/img/Apps/PocketCode/Tower/Tower2.jpg" alt="App 1 Image 3">
</div>
</div>
<div class="carousel-controls">
<button class="carousel-button prev">&lt;</button>
<button class="carousel-button next">&gt;</button>
</div>
</div>
</div>
<script>
const carousel = document.querySelector('.carousel');
const items = document.querySelectorAll('.carousel-item');
const prevButton = document.querySelector('.prev');
const nextButton = document.querySelector('.next');
let currentItem = 2; // Start with the middle item
function updateCarousel() {
items.forEach((item, index) => {
if (index === currentItem - 1) {
// Center item
item.style.transform = 'translateX(-50%)';
} else if (index < currentItem - 1) {
// Items to the left
item.style.transform = `translateX(${-150 - (currentItem - index - 2) * 100}%) translateY(20px) scale(0.8) rotateY(-30deg)`;
} else {
// Items to the right
item.style.transform = `translateX(${50 + (index - currentItem) * 100}%) translateY(20px) scale(0.8) rotateY(30deg)`;
}
});
}
prevButton.addEventListener('click', () => {
currentItem--;
if (currentItem < 1) {
currentItem = items.length;
}
updateCarousel();
});
nextButton.addEventListener('click', () => {
currentItem++;
if (currentItem > items.length) {
currentItem = 1;
}
updateCarousel();
});
// Initial setup
updateCarousel();
</script>
</body>
</html>

View File

@@ -1,334 +0,0 @@
<!DOCTYPE html>
<html lang="de">
<head>
<link rel="icon" href="/img/favicon.jpg" type="image/png">
<meta charset="UTF-8">
<title>BR0TCRAFT</title>
<style>
@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;
margin: 0;
overflow-x: hidden;
}
.header {
background-color: #330000;
padding: 20px;
text-align: center;
border-bottom: 5px solid #660000;
position: fixed;
top: 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);
}
.container {
padding-top: 80px;
display: flex;
flex-wrap: wrap;
justify-content: center;
perspective: 1000px;
}
.section {
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: 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: left;
font-family: 'Orbitron', sans-serif;
color: #ffffff;
text-shadow: 0px 2px 5px rgba(255, 0, 0, 0.5);
}
.content {
display: flex;
align-items: center;
width: 100%;
justify-content: space-between;
}
.item {
background-color: #220000;
color: #ffcccc;
border-radius: 10px;
box-shadow: 0px 2px 5px rgba(255, 0, 0, 0.2);
width: 200px;
height: 200px;
display: flex;
align-items: center;
justify-content: center;
position: relative;
}
.item-content {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
padding: 10px;
}
.item-content img {
max-width: 100%;
max-height: 100%;
object-fit: cover;
border-radius: 10px;
}
.buttons {
display: flex;
flex-direction: column;
justify-content: center;
align-items: flex-start;
margin-left: 20px;
width: calc(100% - 220px);
height: 200px;
}
.button {
background-color: #660000;
color: #ffffff;
padding: 10px 0px;
border: none;
border-radius: 5px;
margin-bottom: 10px;
font-family: 'Roboto', sans-serif;
font-size: 1.2em;
text-align: center;
text-decoration: none;
box-shadow: 0px 2px 5px rgba(255, 0, 0, 0.2);
cursor: pointer;
transition: background-color 0.2s, box-shadow 0.2s;
width: 100%;
}
.button:hover {
background-color: #ff6666;
box-shadow: 0px 4px 10px rgba(255, 0, 0, 0.4);
}
.description {
margin-top: 20px; /* Space from the carousel */
padding: 15px;
background-color: rgba(0, 0, 0, 0.3); /* Semi-transparent background */
border-radius: 10px;
border: 2px solid #660000; /* Red border */
}
.description p {
color: #ffcccc; /* Light red text color */
font-size: 1.1em;
line-height: 1.6;
}
@media (max-width: 800px) {
.content {
flex-direction: column;
align-items: center;
}
.item {
width: 100%;
height: auto;
margin-bottom: 20px;
}
.buttons {
width: 100%;
margin-left: 0;
height: auto;
}
}
.carousel-container {
width: 100%;
display: flex;
justify-content: center;
padding: 20px;
perspective: 1200px;
position: relative;
height: 250px;
overflow: hidden;
margin-top: 20px;
}
.carousel {
display: flex;
transition: transform 0.5s ease;
transform-style: preserve-3d;
width: 100%;
height: 100%;
position: relative;
}
.carousel-item {
width: 250px;
height: 100%;
box-sizing: border-box;
transition: transform 0.5s ease, opacity 0.5s ease;
position: absolute;
top: 0;
left: 50%;
opacity: 1;
}
.carousel-item:nth-child(1) {
transform: translateX(-150%) translateY(20px) scale(0.8) rotateY(-30deg);
}
.carousel-item:nth-child(2) {
transform: translateX(-50%);
}
.carousel-item:nth-child(3) {
transform: translateX(50%) translateY(20px) scale(0.8) rotateY(30deg);
}
.carousel-item img {
width: 100%;
height: 100%;
border-radius: 10px;
display: block;
object-fit: cover;
}
.carousel-controls {
display: flex;
justify-content: space-between;
position: absolute;
top: 50%;
left: 0;
transform: translateY(-50%);
width: 100%;
padding: 0; /* Remove padding */
}
.carousel-button {
background-color: #660000;
color: #ffffff;
border: none;
border-radius: 50%;
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.5em;
cursor: pointer;
transition: background-color 0.2s, box-shadow 0.2s;
margin: 0 10px; /* Add margin */
}
.carousel-button:hover {
background-color: #ff6666;
box-shadow: 0px 4px 10px rgba(255, 0, 0, 0.4);
}
</style>
</head>
<body>
<div class="header">
<a href="/" style="text-decoration: none; color: inherit;">
<h1>BR0TCRAFT</h1>
</a>
</div>
<div class="container">
<div class="section">
<div class="section-title">auto-clicker</div>
<div class="content">
<div class="item">
<div class="item-content">
<img src="/img/Apps/PocketCode/auto-clicker/auto-clicker.png" alt="App 1 Image">
</div>
</div>
<div class="buttons">
<a href="https://share.catrob.at/app/project/13f85a59-ae25-416f-a9b8-33ad1ab3a430" class="button">Spielen/Herunterladen</a>
</div>
</div>
<div class="description">
<p>Ein meiner Meinung nach sehr einfach zu bedienender auto-clicker</p>
<p>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</p>
<p> Spielbar nur am Handy mit der App <a href="/enviroments/PocketCode/index.html">Pocket Code</a></p>
</div>
<div class="carousel-container">
<div class="carousel">
<div class="carousel-item">
<img src="/img/Apps/PocketCode/auto-clicker/clicker1.jpg" alt="App 1 Image 1">
</div>
<div class="carousel-item">
<img src="/img/Apps/PocketCode/auto-clicker/auto-clicker.png" alt="App 1 Image 2">
</div>
<div class="carousel-item">
<img src="/img/Apps/PocketCode/auto-clicker/clicker2.jpg" alt="App 1 Image 3">
</div>
</div>
<div class="carousel-controls">
<button class="carousel-button prev">&lt;</button>
<button class="carousel-button next">&gt;</button>
</div>
</div>
</div>
<script>
const carousel = document.querySelector('.carousel');
const items = document.querySelectorAll('.carousel-item');
const prevButton = document.querySelector('.prev');
const nextButton = document.querySelector('.next');
let currentItem = 2; // Start with the middle item
function updateCarousel() {
items.forEach((item, index) => {
if (index === currentItem - 1) {
// Center item
item.style.transform = 'translateX(-50%)';
} else if (index < currentItem - 1) {
// Items to the left
item.style.transform = `translateX(${-150 - (currentItem - index - 2) * 100}%) translateY(20px) scale(0.8) rotateY(-30deg)`;
} else {
// Items to the right
item.style.transform = `translateX(${50 + (index - currentItem) * 100}%) translateY(20px) scale(0.8) rotateY(30deg)`;
}
});
}
prevButton.addEventListener('click', () => {
currentItem--;
if (currentItem < 1) {
currentItem = items.length;
}
updateCarousel();
});
nextButton.addEventListener('click', () => {
currentItem++;
if (currentItem > items.length) {
currentItem = 1;
}
updateCarousel();
});
// Initial setup
updateCarousel();
</script>
</body>
</html>

View File

@@ -1,332 +0,0 @@
<!DOCTYPE html>
<html lang="de">
<head>
<link rel="icon" href="/img/favicon.jpg" type="image/png">
<meta charset="UTF-8">
<title>BR0TCRAFT</title>
<style>
@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;
margin: 0;
overflow-x: hidden;
}
.header {
background-color: #330000;
padding: 20px;
text-align: center;
border-bottom: 5px solid #660000;
position: fixed;
top: 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);
}
.container {
padding-top: 80px;
display: flex;
flex-wrap: wrap;
justify-content: center;
perspective: 1000px;
}
.section {
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: 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: left;
font-family: 'Orbitron', sans-serif;
color: #ffffff;
text-shadow: 0px 2px 5px rgba(255, 0, 0, 0.5);
}
.content {
display: flex;
align-items: center;
width: 100%;
justify-content: space-between;
}
.item {
background-color: #220000;
color: #ffcccc;
border-radius: 10px;
box-shadow: 0px 2px 5px rgba(255, 0, 0, 0.2);
width: 200px;
height: 200px;
display: flex;
align-items: center;
justify-content: center;
position: relative;
}
.item-content {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
padding: 10px;
}
.item-content img {
max-width: 100%;
max-height: 100%;
object-fit: cover;
border-radius: 10px;
}
.buttons {
display: flex;
flex-direction: column;
justify-content: center;
align-items: flex-start;
margin-left: 20px;
width: calc(100% - 220px);
height: 200px;
}
.button {
background-color: #660000;
color: #ffffff;
padding: 10px 0px;
border: none;
border-radius: 5px;
margin-bottom: 10px;
font-family: 'Roboto', sans-serif;
font-size: 1.2em;
text-align: center;
text-decoration: none;
box-shadow: 0px 2px 5px rgba(255, 0, 0, 0.2);
cursor: pointer;
transition: background-color 0.2s, box-shadow 0.2s;
width: 100%;
}
.button:hover {
background-color: #ff6666;
box-shadow: 0px 4px 10px rgba(255, 0, 0, 0.4);
}
.description {
margin-top: 20px; /* Space from the carousel */
padding: 15px;
background-color: rgba(0, 0, 0, 0.3); /* Semi-transparent background */
border-radius: 10px;
border: 2px solid #660000; /* Red border */
}
.description p {
color: #ffcccc; /* Light red text color */
font-size: 1.1em;
line-height: 1.6;
}
@media (max-width: 800px) {
.content {
flex-direction: column;
align-items: center;
}
.item {
width: 100%;
height: auto;
margin-bottom: 20px;
}
.buttons {
width: 100%;
margin-left: 0;
height: auto;
}
}
.carousel-container {
width: 100%;
display: flex;
justify-content: center;
padding: 20px;
perspective: 1200px;
position: relative;
height: 250px;
overflow: hidden;
margin-top: 20px;
}
.carousel {
display: flex;
transition: transform 0.5s ease;
transform-style: preserve-3d;
width: 100%;
height: 100%;
position: relative;
}
.carousel-item {
width: 250px;
height: 100%;
box-sizing: border-box;
transition: transform 0.5s ease, opacity 0.5s ease;
position: absolute;
top: 0;
left: 50%;
opacity: 1;
}
.carousel-item:nth-child(1) {
transform: translateX(-150%) translateY(20px) scale(0.8) rotateY(-30deg);
}
.carousel-item:nth-child(2) {
transform: translateX(-50%);
}
.carousel-item:nth-child(3) {
transform: translateX(50%) translateY(20px) scale(0.8) rotateY(30deg);
}
.carousel-item img {
width: 100%;
height: 100%;
border-radius: 10px;
display: block;
object-fit: cover;
}
.carousel-controls {
display: flex;
justify-content: space-between;
position: absolute;
top: 50%;
left: 0;
transform: translateY(-50%);
width: 100%;
padding: 0; /* Remove padding */
}
.carousel-button {
background-color: #660000;
color: #ffffff;
border: none;
border-radius: 50%;
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.5em;
cursor: pointer;
transition: background-color 0.2s, box-shadow 0.2s;
margin: 0 10px; /* Add margin */
}
.carousel-button:hover {
background-color: #ff6666;
box-shadow: 0px 4px 10px rgba(255, 0, 0, 0.4);
}
</style>
</head>
<body>
<div class="header">
<a href="/" style="text-decoration: none; color: inherit;">
<h1>BR0TCRAFT</h1>
</a>
</div>
<div class="container">
<div class="section">
<div class="section-title">Tiny Snake</div>
<div class="content">
<div class="item">
<div class="item-content">
<img src="/img/Apps/snake.jpg" alt="App 1 Image">
</div>
</div>
<div class="buttons">
<a href="/play/Tiny-Snake/snake.html" class="button">Spielen</a>
<a href="#" class="button">Herunterladen</a>
<a href="#" class="button">Github-Repository</a>
</div>
</div>
<div class="description">
<p>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</p>
</div>
<div class="carousel-container">
<div class="carousel">
<div class="carousel-item">
<img src="/img/Apps/Snake/snake1.PNG" alt="App 1 Image 1">
</div>
<div class="carousel-item">
<img src="/img/Apps/snake.jpg" alt="App 1 Image 2">
</div>
<div class="carousel-item">
<img src="/img/Apps/Snake/Snake2.png" alt="App 1 Image 3">
</div>
</div>
<div class="carousel-controls">
<button class="carousel-button prev">&lt;</button>
<button class="carousel-button next">&gt;</button>
</div>
</div>
</div>
<script>
const carousel = document.querySelector('.carousel');
const items = document.querySelectorAll('.carousel-item');
const prevButton = document.querySelector('.prev');
const nextButton = document.querySelector('.next');
let currentItem = 2; // Start with the middle item
function updateCarousel() {
items.forEach((item, index) => {
if (index === currentItem - 1) {
// Center item
item.style.transform = 'translateX(-50%)';
} else if (index < currentItem - 1) {
// Items to the left
item.style.transform = `translateX(${-150 - (currentItem - index - 2) * 100}%) translateY(20px) scale(0.8) rotateY(-30deg)`;
} else {
// Items to the right
item.style.transform = `translateX(${50 + (index - currentItem) * 100}%) translateY(20px) scale(0.8) rotateY(30deg)`;
}
});
}
prevButton.addEventListener('click', () => {
currentItem--;
if (currentItem < 1) {
currentItem = items.length;
}
updateCarousel();
});
nextButton.addEventListener('click', () => {
currentItem++;
if (currentItem > items.length) {
currentItem = 1;
}
updateCarousel();
});
// Initial setup
updateCarousel();
</script>
</body>
</html>

378
article.html Normal file
View File

@@ -0,0 +1,378 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>BR0TCRAFT | Artikel</title>
<link rel="stylesheet" href="style.css">
<link
href="https://fonts.googleapis.com/css2?family=Fira+Code:wght@400;600&family=Inter:wght@300;400;600;800&family=Orbitron:wght@700&display=swap"
rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/atom-one-dark.min.css">
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
</head>
<body>
<nav class="navbar">
<div class="brand">BR0TCRAFT</div>
<div class="nav-links">
<a href="index.html">Home</a>
<a href="projects.html">Projekte</a>
<a href="tutorials.html" class="active">Dev Notes</a>
<a href="about.html">About</a>
<a href="assets.html">Assets</a>
</div>
</nav>
<div class="sidebar-overlay" id="overlay"></div>
<button class="mobile-menu-btn" id="menuToggle">
<span></span>
</button>
<div class="container-docs">
<div class="docs-layout">
<aside class="docs-sidebar" id="sidebar">
<button class="sidebar-toggle-desktop" id="sidebarCollapseBtn">
<span>&larr;</span> Sidebar einklappen
</button>
<div class="sidebar-group">
<h4>Kapitel</h4>
<div id="auto-toc"></div>
</div>
</aside>
<button class="sidebar-collapsed-trigger" id="sidebarExpandBtn" title="Sidebar zeigen">
&rarr;
</button>
<main class="section">
<div id="markdown-content">
<p style="color: #444; font-family: var(--font-code); font-size: 0.85rem;">Lade Inhalt...</p>
</div>
<div id="doc-nav" class="doc-pagination"></div>
<div style="margin-top: 4rem; border-top: 1px solid #1e1e1e; padding-top: 2rem; display: flex; gap: 0.75rem;">
<a href="tutorials.html" class="btn">&larr; Dev Notes</a>
<a href="index.html" class="btn"
style="background: transparent; border: 1px solid #222; color: var(--text-muted);">Home</a>
</div>
</main>
</div>
</div>
<script>
const urlParams = new URLSearchParams(window.location.search);
const fileParam = urlParams.get('file');
const docParam = urlParams.get('doc');
const partParam = urlParams.get('part');
const contentDiv = document.getElementById('markdown-content');
const toc = document.getElementById('auto-toc');
let docManifest = null;
// --- 1. Tabs System ---
function initTabs() {
document.querySelectorAll('.code-tabs').forEach((group, gIdx) => {
const wrapper = document.createElement('div');
wrapper.className = 'tab-wrapper';
const headerRow = document.createElement('div');
headerRow.className = 'tab-header-row';
wrapper.appendChild(headerRow);
const children = Array.from(group.children);
let tabCount = 0;
children.forEach((child) => {
if (child.tagName.startsWith('H')) {
const btn = document.createElement('button');
btn.className = 'tab-trigger';
btn.textContent = child.textContent;
btn.dataset.target = `panel-${gIdx}-${tabCount}`;
if (tabCount === 0) btn.classList.add('active');
btn.onclick = (e) => {
wrapper.querySelectorAll('.tab-trigger').forEach(b => b.classList.remove('active'));
wrapper.querySelectorAll('.tab-panel').forEach(p => p.classList.remove('active'));
e.target.classList.add('active');
wrapper.querySelector(`#${e.target.dataset.target}`).classList.add('active');
};
headerRow.appendChild(btn);
} else if (child.tagName === 'PRE') {
const panel = document.createElement('div');
panel.className = 'tab-panel';
panel.id = `panel-${gIdx}-${tabCount}`;
if (tabCount === 0) panel.classList.add('active');
panel.appendChild(child.cloneNode(true));
wrapper.appendChild(panel);
tabCount++;
}
});
group.parentNode.replaceChild(wrapper, group);
});
}
// --- 2. Code Headers & Copy Button ---
function enhanceCodeBlocks() {
hljs.highlightAll();
document.querySelectorAll('pre code').forEach((block) => {
const parent = block.parentElement;
if (parent.querySelector('.code-header')) return;
let lang = "CODE";
block.classList.forEach(c => { if (c.startsWith('language-')) lang = c.split('-')[1].toUpperCase(); });
const header = document.createElement('div');
header.className = 'code-header';
const copyBtn = document.createElement('button');
copyBtn.className = 'copy-btn';
copyBtn.innerHTML = `<span>Copy</span>`;
copyBtn.onclick = () => {
navigator.clipboard.writeText(block.innerText);
copyBtn.innerHTML = `<span style="color:var(--accent-primary)">Copied!</span>`;
setTimeout(() => copyBtn.innerHTML = `<span>Copy</span>`, 2000);
};
header.innerHTML = `<span class="lang-tag">${lang}</span>`;
header.appendChild(copyBtn);
parent.insertBefore(header, block);
});
}
// --- 3. Sidebar Generators ---
// Auto-generate TOC from headers (for single-file mode)
function generateAutoTOC() {
const headers = contentDiv.querySelectorAll('h2, h3');
headers.forEach(h => {
const link = document.createElement('a');
link.className = 'sidebar-link';
link.href = '#' + h.id;
link.textContent = h.textContent.replace('#', '').trim();
if (h.tagName === 'H3') link.style.paddingLeft = '25px';
toc.appendChild(link);
});
}
// Generate Sidebar from Document Manifest
function renderDocSidebar(chapters, container, depth = 0) {
chapters.forEach(chapter => {
if (chapter.children) {
const groupTitle = document.createElement('div');
groupTitle.className = 'sidebar-category';
groupTitle.textContent = chapter.title;
groupTitle.style.paddingLeft = (depth * 15) + 'px';
const childGroup = document.createElement('div');
childGroup.className = 'sidebar-child-group';
// Check if current part is in this group to keep it open
const hasActiveChild = (list) => list.some(c => c.id === partParam || (c.children && hasActiveChild(c.children)));
if (!hasActiveChild(chapter.children)) {
// groupTitle.classList.add('collapsed');
// childGroup.classList.add('collapsed');
}
groupTitle.onclick = () => {
groupTitle.classList.toggle('collapsed');
childGroup.classList.toggle('collapsed');
};
container.appendChild(groupTitle);
container.appendChild(childGroup);
renderDocSidebar(chapter.children, childGroup, depth + 1);
} else {
const link = document.createElement('a');
link.className = 'sidebar-link';
if (partParam === chapter.id || (!partParam && depth === 0 && chapters.indexOf(chapter) === 0)) {
link.classList.add('active');
}
link.href = `?doc=${docParam}&part=${chapter.id}`;
link.textContent = chapter.title;
link.style.paddingLeft = (depth * 15 + 15) + 'px';
container.appendChild(link);
}
});
}
// --- Anchor Links ---
function addAnchorLinks() {
contentDiv.querySelectorAll('h1, h2, h3').forEach(h => {
const slug = h.textContent.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/(^-|-$)+/g, '');
h.id = slug;
const a = document.createElement('a');
a.className = 'anchor-link';
a.href = '#' + slug;
a.innerHTML = '#';
h.insertBefore(a, h.firstChild);
});
}
// --- Navigation ---
const menuBtn = document.getElementById('menuToggle');
const sidebar = document.getElementById('sidebar');
const overlay = document.getElementById('overlay');
const docsLayout = document.querySelector('.docs-layout');
function toggleMenu() {
menuBtn.classList.toggle('active');
sidebar.classList.toggle('active');
overlay.classList.toggle('active');
}
menuBtn.addEventListener('click', toggleMenu);
overlay.addEventListener('click', toggleMenu);
// Desktop Toggle
document.getElementById('sidebarCollapseBtn')?.addEventListener('click', () => {
docsLayout.classList.add('sidebar-collapsed');
});
document.getElementById('sidebarExpandBtn')?.addEventListener('click', () => {
docsLayout.classList.remove('sidebar-collapsed');
});
document.addEventListener('click', (e) => {
if (e.target.classList.contains('sidebar-link') && window.innerWidth <= 1024) {
toggleMenu();
}
});
// --- Update page title ---
function updateTitle() {
const h1 = contentDiv.querySelector('h1');
if (h1) {
document.title = 'BR0TCRAFT | ' + h1.textContent.replace('#', '').trim();
}
}
// --- Loading Logic ---
function loadMarkdown(path) {
fetch(path)
.then(r => {
if (!r.ok) throw new Error('Datei nicht gefunden');
return r.text();
})
.then(md => {
contentDiv.innerHTML = marked.parse(md);
updateTitle();
addAnchorLinks();
initTabs();
enhanceCodeBlocks();
if (!docParam) generateAutoTOC();
window.scrollTo(0, 0);
})
.catch(() => {
contentDiv.innerHTML = `<p style="color:#555; font-family:var(--font-code); font-size:0.85rem;">
Inhalt konnte nicht geladen werden.
</p>`;
});
}
// --- Navigation Footer ---
function renderPagination() {
if (!docManifest) return;
const nav = document.getElementById('doc-nav');
nav.innerHTML = '';
// Flatten structure to get linear sequence
const flat = [];
const flatten = (list) => {
list.forEach(item => {
if (item.file) flat.push(item);
if (item.children) flatten(item.children);
});
};
flatten(docManifest.chapters);
const currentIndex = flat.findIndex(item => item.id === (partParam || flat[0].id));
if (currentIndex > 0) {
const prev = flat[currentIndex - 1];
const a = document.createElement('a');
a.href = `?doc=${docParam}&part=${prev.id}`;
a.className = 'nav-page-btn prev';
a.innerHTML = `<span class="nav-label">Zurück</span><span class="nav-title">${prev.title}</span>`;
nav.appendChild(a);
} else {
nav.appendChild(document.createElement('div')); // Spacer
}
if (currentIndex < flat.length - 1) {
const next = flat[currentIndex + 1];
const a = document.createElement('a');
a.href = `?doc=${docParam}&part=${next.id}`;
a.className = 'nav-page-btn next';
a.innerHTML = `<span class="nav-label">Weiter</span><span class="nav-title">${next.title}</span>`;
nav.appendChild(a);
}
}
if (docParam) {
const manifestPath = `content/${docParam}/index.json`;
fetch(manifestPath)
.then(r => {
if (!r.ok) throw new Error('Manifest nicht gefunden');
return r.json();
})
.then(data => {
docManifest = data;
document.querySelector('#sidebar h4').textContent = data.title || "Kapitel";
renderDocSidebar(data.chapters, toc);
let targetFile = null;
const findPart = (list) => {
for (const item of list) {
if (item.id === partParam) { targetFile = item.file; break; }
if (item.children) findPart(item.children);
if (targetFile) break;
}
};
if (partParam) {
findPart(data.chapters);
} else {
const first = data.chapters[0];
targetFile = first.children ? first.children[0].file : first.file;
}
if (targetFile) {
loadMarkdown(`content/${docParam}/${targetFile}`);
renderPagination();
}
})
.catch(err => {
contentDiv.innerHTML = `<div style="background: rgba(255,77,77,0.05); border: 1px solid var(--accent-primary); padding: 2rem; border-radius: 12px; margin-top: 2rem;">
<h3 style="color: var(--accent-primary); margin-top: 0;">Ladefehler</h3>
<p style="color:#aaa; font-size:0.9rem;">Die Dokumentation <b>${docParam}</b> konnte nicht geladen werden.</p>
<ul style="color:#777; font-size:0.8rem; margin-top: 1rem;">
<li>Prüfe, ob du die Seite über einen <b>Webserver</b> aufrufst (nicht nur lokal öffnen).</li>
<li>Prüfe, ob die Datei <code>content/${docParam}/index.json</code> existiert.</li>
</ul>
</div>`;
});
} else if (fileParam) {
loadMarkdown(fileParam);
} else {
contentDiv.innerHTML = `<p style="color:#555; font-family:var(--font-code); font-size:0.85rem;">
Kein Artikel angegeben.
</p>`;
}
</script>
</body>
</html>

290
assets.html Normal file
View File

@@ -0,0 +1,290 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>BR0TCRAFT | Assets</title>
<link rel="stylesheet" href="style.css">
<link
href="https://fonts.googleapis.com/css2?family=Fira+Code:wght@400;600&family=Inter:wght@300;400;600;800&family=Orbitron:wght@700&display=swap"
rel="stylesheet">
<style>
.license-badge {
display: inline-flex;
align-items: center;
gap: 0.5rem;
background: rgba(255, 77, 77, 0.07);
border: 1px solid rgba(255, 77, 77, 0.2);
border-radius: 6px;
padding: 0.4rem 0.9rem;
font-family: var(--font-code);
font-size: 0.78rem;
color: var(--accent-primary);
margin-bottom: 1.75rem;
}
.assets-intro {
max-width: 600px;
color: var(--text-muted);
line-height: 1.75;
font-size: 0.95rem;
margin-bottom: 3rem;
}
.assets-intro p {
margin-bottom: 0.75rem;
}
/* Asset grid */
.asset-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
gap: 0.75rem;
margin-top: 1.5rem;
}
.asset-item {
border: 1px solid #1f1f1f;
border-radius: var(--radius);
overflow: hidden;
background: rgba(255, 255, 255, 0.015);
transition: border-color 0.2s;
cursor: pointer;
}
.asset-item:hover {
border-color: #333;
}
.asset-preview {
height: 100px;
background: #111;
display: flex;
align-items: center;
justify-content: center;
font-size: 2rem;
}
.asset-preview img {
max-width: 80%;
max-height: 80%;
object-fit: contain;
filter: brightness(0.9);
}
.asset-meta {
padding: 0.6rem 0.8rem;
border-top: 1px solid #1a1a1a;
}
.asset-name {
font-family: var(--font-code);
font-size: 0.75rem;
color: #888;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.asset-type {
font-family: var(--font-code);
font-size: 0.65rem;
color: #444;
margin-top: 2px;
}
/* Category filter */
.filter-row {
display: flex;
gap: 0.4rem;
flex-wrap: wrap;
margin-bottom: 1.5rem;
}
.filter-btn {
background: none;
border: 1px solid #222;
border-radius: 4px;
color: #555;
font-family: var(--font-code);
font-size: 0.75rem;
padding: 0.3rem 0.8rem;
cursor: pointer;
transition: 0.2s;
}
.filter-btn.active,
.filter-btn:hover {
border-color: var(--accent-primary);
color: var(--accent-primary);
}
.empty-state {
color: #444;
font-family: var(--font-code);
font-size: 0.85rem;
padding: 3rem 0;
text-align: center;
}
.empty-state span {
display: block;
font-size: 2rem;
margin-bottom: 0.75rem;
}
/* Info box */
.info-box {
border: 1px solid #1e1e1e;
border-radius: var(--radius);
padding: 1.25rem 1.5rem;
background: rgba(255, 255, 255, 0.015);
max-width: 600px;
margin-top: 3rem;
}
.info-box h4 {
font-family: var(--font-code);
font-size: 0.78rem;
color: var(--accent-primary);
margin-bottom: 0.6rem;
letter-spacing: 0.06em;
}
.info-box p {
color: var(--text-muted);
font-size: 0.875rem;
line-height: 1.65;
}
</style>
</head>
<body>
<nav class="navbar">
<div class="brand">BR0TCRAFT</div>
<div class="nav-links">
<a href="index.html">Home</a>
<a href="projects.html">Projekte</a>
<a href="tutorials.html">Dev Notes</a>
<a href="about.html">About</a>
<a href="assets.html" class="active">Assets</a>
</div>
</nav>
<div class="container">
<section class="section">
<h1 style="font-family: var(--font-display); margin-bottom: 1.25rem; color: #fff;">Assets</h1>
<div class="license-badge">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="12" cy="12" r="10" />
<path d="M12 8v4m0 4h.01" />
</svg>
CC0 Public Domain
</div>
<div class="assets-intro">
<p>
Icons, Grafiken und kleine UI-Assets, die ich für eigene Projekte erstellt oder gesammelt habe.
Alles steht unter <strong style="color:#fff">CC0</strong>,
keine Namensnennung erforderlich, keine Einschränkungen bei der Nutzung.
</p>
<p>
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.
</p>
<p>
Mitnehmen, anpassen, verwenden.
</p>
</div>
<div class="filter-row">
<button class="filter-btn active" onclick="filterAssets('all', this)">Alle</button>
<button class="filter-btn" onclick="filterAssets('icon', this)">Icons</button>
<button class="filter-btn" onclick="filterAssets('texture', this)">Texturen</button>
<button class="filter-btn" onclick="filterAssets('ui', this)">UI</button>
</div>
<div class="asset-grid" id="assetGrid">
<div class="asset-item" data-category="icon">
<div class="asset-preview">🔧</div>
<div class="asset-meta">
<div class="asset-name">tool.svg</div>
<div class="asset-type">SVG · Icon</div>
</div>
</div>
<div class="asset-item" data-category="icon">
<div class="asset-preview">⚙️</div>
<div class="asset-meta">
<div class="asset-name">settings.svg</div>
<div class="asset-type">SVG · Icon</div>
</div>
</div>
<div class="asset-item" data-category="ui">
<div class="asset-preview">🎨</div>
<div class="asset-meta">
<div class="asset-name">button-set.png</div>
<div class="asset-type">PNG · UI</div>
</div>
</div>
<div class="asset-item" data-category="texture">
<div class="asset-preview"
style="background: repeating-linear-gradient(45deg, #111 0px, #111 2px, #0d0d0d 2px, #0d0d0d 8px);"></div>
<div class="asset-meta">
<div class="asset-name">noise-dark.png</div>
<div class="asset-type">PNG · Textur</div>
</div>
</div>
<div class="asset-item" data-category="icon">
<div class="asset-preview">📁</div>
<div class="asset-meta">
<div class="asset-name">folder.svg</div>
<div class="asset-type">SVG · Icon</div>
</div>
</div>
<div class="asset-item" data-category="ui">
<div class="asset-preview" style="background: linear-gradient(135deg, #1a0000, #220000);">
<div style="width:60%; height:8px; background:#ff4d4d22; border-radius:4px;"></div>
</div>
<div class="asset-meta">
<div class="asset-name">progress-bar.svg</div>
<div class="asset-type">SVG · UI</div>
</div>
</div>
</div>
<div class="info-box">
<h4>// Eigene Assets beisteuern?</h4>
<p>
Wenn du selbst freie Assets beisteuern willst, meld dich per
<a href="mailto:info@br0tcraft.de" style="color: var(--accent-primary); text-decoration: none;">Email</a>
oder über GitHub. CC0 ist Bedingung.
</p>
</div>
</section>
</div>
<script>
function filterAssets(category, btn) {
document.querySelectorAll('.filter-btn').forEach(b => b.classList.remove('active'));
btn.classList.add('active');
document.querySelectorAll('.asset-item').forEach(item => {
const show = category === 'all' || item.dataset.category === category;
item.style.display = show ? '' : 'none';
});
}
</script>
</body>
</html>

25
content/3ds/index.json Normal file
View File

@@ -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"
}
]
}
]
}

32
content/3ds/intro copy.md Normal file
View File

@@ -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

30
content/3ds/intro.md Normal file
View File

@@ -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.

View File

@@ -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.

View File

@@ -0,0 +1 @@
# Einleitung\nDas ist der erste Teil einer strukturierten Dokumentation.

View File

@@ -0,0 +1 @@
# Installation\nHier steht, wie man alles installiert.

View File

@@ -0,0 +1 @@
# Performance\nOptimierungstipps für Profis.

View File

@@ -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" }
]
}
]
}

View File

@@ -1,311 +0,0 @@
<!DOCTYPE html>
<html lang="de">
<head>
<link rel="icon" href="/img/favicon.jpg" type="image/png">
<meta charset="UTF-8">
<title>BR0TCRAFT</title>
<style>
@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;
margin: 0;
overflow-x: hidden;
}
.header {
background-color: #330000;
padding: 20px;
text-align: center;
border-bottom: 5px solid #660000;
position: fixed;
top: 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);
}
.container {
padding-top: 80px;
display: flex;
flex-wrap: wrap;
justify-content: center;
perspective: 1000px;
}
.section {
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: 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: left;
font-family: 'Orbitron', sans-serif;
color: #ffffff;
text-shadow: 0px 2px 5px rgba(255, 0, 0, 0.5);
}
.content {
display: flex;
align-items: center;
width: 100%;
justify-content: space-between;
}
.item {
background-color: #220000;
color: #ffcccc;
border-radius: 10px;
box-shadow: 0px 2px 5px rgba(255, 0, 0, 0.2);
width: 200px;
height: 200px;
display: flex;
align-items: center;
justify-content: center;
position: relative;
}
.item-content {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
padding: 10px;
}
.item-content img {
max-width: 100%;
max-height: 100%;
object-fit: cover;
border-radius: 10px;
}
.buttons {
display: flex;
flex-direction: column;
justify-content: center;
align-items: flex-start;
margin-left: 20px;
width: calc(100% - 220px);
height: 200px;
}
.button {
background-color: #660000;
color: #ffffff;
padding: 10px 0px;
border: none;
border-radius: 5px;
margin-bottom: 10px;
font-family: 'Roboto', sans-serif;
font-size: 1.2em;
text-align: center;
text-decoration: none;
box-shadow: 0px 2px 5px rgba(255, 0, 0, 0.2);
cursor: pointer;
transition: background-color 0.2s, box-shadow 0.2s;
width: 100%;
}
.button:hover {
background-color: #ff6666;
box-shadow: 0px 4px 10px rgba(255, 0, 0, 0.4);
}
.description {
margin-top: 20px; /* Space from the carousel */
padding: 15px;
background-color: rgba(0, 0, 0, 0.3); /* Semi-transparent background */
border-radius: 10px;
border: 2px solid #660000; /* Red border */
}
.description p {
color: #ffcccc; /* Light red text color */
font-size: 1.1em;
line-height: 1.6;
}
.item-content {
position: relative;
width: 100%;
height: 100%;
padding: 10px; /* Spacing around the content */
}
.item-content2 img {
position: absolute;
top: 0;
left: 25%;
width: 50%;
margin: auto;
height: auto;
max-height: calc(100% - 0px); /* Adjust image height here */
top: 5px;
object-fit: contain;/* Cover the area and maintain aspect ratio */
border-radius: 10px; /* Round all corners of the image */
}
.devvlog-item {
height: 200px;
}
.item-text {
position: absolute;
bottom: 10px; /* Space from the bottom */
left: 0;
width: 100%;
text-align: center;
font-size: 1.8em;
}
.item:hover {
box-shadow: 0px 5px 15px rgba(255, 0, 0, 0.5);
}
.item::before {
content: '';
position: absolute;
top: -1px;
left: -1px;
right: -1px;
bottom: -1px;
border: 1px solid #ff6666;
border-radius: 11px;
opacity: 0;
transition: opacity 0.2s;
}
.item:hover::before {
opacity: 1;
}
.item2 {
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;
display: flex;
align-items: center;
justify-content: center;
position: relative;
}
.section2 {
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-title2 {
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);
}
@media (max-width: 800px) {
.content {
flex-direction: column;
align-items: center;
}
.item {
width: 100%;
height: auto;
margin-bottom: 20px;
}
.buttons {
width: 100%;
margin-left: 0;
height: auto;
}
}
</style>
</head>
<body>
<div class="header">
<a href="/" style="text-decoration: none; color: inherit;">
<h1>BR0TCRAFT</h1>
</a>
</div>
<div class="container">
<div class="section">
<div class="section-title">Pocket Code</div>
<div class="content">
<div class="item">
<div class="item-content">
<img src="/img/developmentEnvironment/PocketCodetiny.png" alt="App 1 Image">
</div>
</div>
<div class="buttons">
<a href="https://play.google.com/store/apps/details?id=org.catrobat.catroid&hl=de&pli=1" class="button">Herunterladen</a>
<a href="https://share.catrob.at/app/user/3a747897-7a83-11eb-a92d-005056a36f47" class="button">Mein Catrobat-Acount</a>
</div>
</div>
<div class="description">
<p>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.</p>
</div>
</div>
<div class="section2">
<div class="section-title2">Meine Apps</div>
<a href="/" class="item devvlog-item">
<div class="item-content2">
<img src="img/Apps/Earthlarge.png" alt="App 1 Image">
<div class="item-text">Ki-Chatbot</div>
</div>
</a>
<a href="/apps/PocketCode/Tower-defense/index.html" class="item devvlog-item">
<div class="item-content2">
<img src="/img/Apps/PocketCode/Tower/Tower.png" alt="App 1 Image">
<div class="item-text">Tower defense (Beta)</div>
</div>
</a>
<a href="/apps/PocketCode/auto-clicker/index.html" class="item devvlog-item">
<div class="item-content2">
<img src="/img/Apps/PocketCode/auto-clicker/auto-clicker.png" alt="App 1 Image">
<div class="item-text">auto-clicker</div>
</div>
<a href="/apps/PocketCode/Sensor-datas/index.html" class="item devvlog-item">
<div class="item-content2">
<img src="/img/Apps/PocketCode/Sensor/Sensor.png" alt="App 1 Image">
<div class="item-text">Sensor Datas</div>
</div>
</a>
</a>
</div>
</body>
</html>

617
ext/scene-manager.js Normal file
View File

@@ -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 = '<div class="sm-tree-empty">No .sb3 files found in this folder.</div>';
} else {
for (const [dir, items] of Object.entries(grouped)) {
if (dir) treeHtml += `<div class="sm-folder-row">📁 ${this._esc(dir)}/</div>`;
for (const item of items) {
const name = item.path.split('/').pop().replace(/\.sb3$/i, '');
treeHtml += `<div class="sm-file-row" data-path="${this._esc(item.path)}">📄 ${this._esc(name)}.sb3</div>`;
}
}
}
box.innerHTML = `
<h2>Browse Projects</h2>
<p>Root folder: <code>${this._esc(rootDirectoryHandle.name)}/</code></p>
<div class="sm-tree" id="sm-tree">${treeHtml}</div>
<p id="sm-sel-label" style="font-size:12.5px;color:#585b70;min-height:18px;margin-bottom:0"></p>
<div class="sm-row">
<button class="sm-btn sm-btn-ghost" id="sm-cancel-btn">Cancel</button>
<button class="sm-btn sm-btn-primary" id="sm-switch-btn" disabled>Switch →</button>
</div>
`;
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 = `
<h2>How the Scene Manager works</h2>
<p>
<strong style="color:#cdd6f4">Scene Manager</strong> lets you split your Scratch project
into multiple <code>.sb3</code> files and seamlessly switch between them at runtime —
just like loading a new level or screen.
</p>
<div class="sm-note">
<strong>Scratch Everywhere</strong><br>
All <code>.sb3</code> files directly inside the scratch-everywhere folder appear in the SE! menu.<br><br>
<strong>Set Root Folder</strong> 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.
</div>
<hr class="sm-sep">
<p><strong style="color:#cdd6f4">Recommended project structure</strong></p>
<div class="sm-path">📁 scratch-everywhere/
MyGame.sb3 ← launcher (appears in menu)
📁 MyGameName-Scenes/
Level1.sb3
Level2.sb3
BossLevel.sb3</div>
<p>
Only <code>MyGame.sb3</code> is visible in the game-selection menu.
It uses Scene Manager blocks to load the actual levels from the
<code>scenes/</code> subfolder, those files stay private inside the SE! menu.
</p>
<div class="sm-note">
Use <strong>subfolder-prefixed paths</strong> in the scene blocks:<br>
<code>open scenes/Level1.sb3</code><br>
Scene Manager navigates into the subfolder automatically.
</div>
<div class="sm-row">
<button class="sm-btn sm-btn-primary" id="sm-ok-btn">Got it!</button>
</div>
`;
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 = `
<h2>Root folder set</h2>
<p>Scene Manager will now load projects from:</p>
<div class="sm-path">📁 ${this._esc(name)}/</div>
<div class="sm-note">
In <strong>Scratch Everywhere</strong> this corresponds to the
<code>scratch-everywhere/</code> 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.
</div>
<div class="sm-row">
<button class="sm-btn sm-btn-primary" id="sm-ok-btn">Got it!</button>
</div>
`;
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 = `
<h2>Root folder required</h2>
<p>
<strong style="color:#cdd6f4">${this._esc(action)}</strong> needs to know
where your <code>.sb3</code> files are stored.
</p>
<div class="sm-note">
Select your <code>scratch-everywhere/</code> folder (or any folder containing your projects).
After selecting, the action will continue automatically.
</div>
<div class="sm-row">
<button class="sm-btn sm-btn-ghost" id="sm-cancel-btn">Cancel</button>
<button class="sm-btn sm-btn-primary" id="sm-select-btn">Select folder…</button>
</div>
`;
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
? `<button class="sm-btn sm-btn-primary" id="sm-save-btn">Save current & switch</button>`
: `<button class="sm-btn sm-btn-primary" id="sm-switch-btn">Switch</button>`;
box.innerHTML = `
<h2>Switch scene</h2>
<p>Load <code>${this._esc(display)}</code>?</p>
<div class="sm-note">
Unsaved changes in the current project will be lost.
${currentFileHandle ? 'You can save first using the button below.' : ''}
</div>
<div class="sm-row">
<button class="sm-btn sm-btn-ghost" id="sm-cancel-btn">Cancel</button>
${saveRow}
${currentFileHandle ? `<button class="sm-btn sm-btn-warn" id="sm-switch-btn">Switch without saving</button>` : ''}
</div>
`;
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 = `
<h2>Error</h2>
<p>${this._esc(msg)}</p>
<div class="sm-row">
<button class="sm-btn sm-btn-primary" id="sm-ok-btn">OK</button>
</div>
`;
box.querySelector('#sm-ok-btn').onclick = () => close(null);
});
}
_esc(str) {
return String(str)
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;');
}
}
Scratch.extensions.register(new SceneManager());
})(Scratch);

388
extensions.html Normal file
View File

@@ -0,0 +1,388 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>BR0TCRAFT | Extensions</title>
<link rel="stylesheet" href="style.css">
<link
href="https://fonts.googleapis.com/css2?family=Fira+Code:wght@400;600&family=Inter:wght@300;400;600;800&family=Orbitron:wght@700&display=swap"
rel="stylesheet">
<style>
/* Search & Filter Bar */
.controls-container {
display: flex;
flex-direction: column;
gap: 1rem;
margin-bottom: 2rem;
background: rgba(255, 255, 255, 0.02);
border: 1px solid #2a2a2a;
border-radius: var(--radius);
padding: 1.5rem;
}
.search-input {
width: 100%;
background: #111;
border: 1px solid #333;
color: #fff;
font-family: var(--font-code);
padding: 10px 15px;
border-radius: 6px;
font-size: 0.9rem;
transition: 0.2s;
}
.search-input:focus {
outline: none;
border-color: var(--accent-primary);
box-shadow: 0 0 10px rgba(255, 77, 77, 0.1);
}
.filter-tags {
display: flex;
gap: 0.5rem;
flex-wrap: wrap;
}
.filter-tag {
background: transparent;
border: 1px solid #333;
color: var(--text-muted);
font-family: var(--font-code);
font-size: 0.75rem;
padding: 4px 10px;
border-radius: 20px;
cursor: pointer;
transition: 0.2s;
}
.filter-tag:hover {
border-color: #555;
color: #fff;
}
.filter-tag.active {
background: rgba(255, 77, 77, 0.1);
border-color: var(--accent-primary);
color: var(--accent-primary);
}
/* Extension Card Styles */
.ext-status {
display: inline-block;
font-family: var(--font-code);
font-size: 0.65rem;
padding: 2px 6px;
border-radius: 3px;
margin-left: 0.5rem;
vertical-align: middle;
text-transform: uppercase;
font-weight: 600;
}
.ext-status-new {
background: rgba(77, 255, 128, 0.1);
color: #4dff80;
border: 1px solid rgba(77, 255, 128, 0.2);
}
.ext-status-updated {
background: rgba(0, 180, 255, 0.1);
color: #00b4ff;
border: 1px solid rgba(0, 180, 255, 0.2);
}
.ext-tags {
display: flex;
gap: 0.4rem;
flex-wrap: wrap;
margin-top: 0.75rem;
}
.ext-tag {
background: rgba(255, 255, 255, 0.05);
font-family: var(--font-code);
font-size: 0.65rem;
color: #aaa;
padding: 2px 6px;
border-radius: 3px;
}
.ext-author {
font-family: var(--font-code);
font-size: 0.7rem;
color: #ffb400;
margin-top: 0.5rem;
display: block;
}
.ext-actions {
display: flex;
gap: 0.6rem;
margin-top: 1rem;
padding-top: 1rem;
border-top: 1px solid #222;
flex-wrap: wrap;
}
.ext-btn {
font-family: var(--font-code);
font-size: 0.75rem;
padding: 6px 12px;
border-radius: 4px;
cursor: pointer;
display: flex;
align-items: center;
gap: 6px;
transition: 0.2s;
text-decoration: none;
}
.btn-copy {
background: rgba(255, 77, 77, 0.1);
border: 1px solid rgba(255, 77, 77, 0.3);
color: var(--accent-primary);
}
.btn-copy:hover {
background: rgba(255, 77, 77, 0.2);
box-shadow: 0 0 10px rgba(255, 77, 77, 0.1);
}
.btn-docs {
background: transparent;
border: 1px solid #444;
color: #ccc;
}
.btn-docs:hover {
border-color: #777;
color: #fff;
}
/* Toast Notification */
.toast {
position: fixed;
bottom: -60px;
left: 50%;
transform: translateX(-50%);
background: var(--accent-primary);
color: #000;
font-weight: 600;
font-family: var(--font-code);
padding: 10px 20px;
border-radius: 20px;
opacity: 0;
transition: 0.3s cubic-bezier(0.4, 0, 0.2, 1);
z-index: 1000;
pointer-events: none;
}
.toast.show {
bottom: 30px;
opacity: 1;
}
.empty-state {
text-align: center;
padding: 4rem 1rem;
color: #555;
font-family: var(--font-code);
grid-column: 1 / -1;
}
</style>
</head>
<body>
<nav class="navbar">
<div class="brand">BR0TCRAFT</div>
<div class="nav-links">
<a href="index.html">Home</a>
<a href="projects.html" class="active">Projekte</a> <!-- Active, as Extensions are a subset -->
<a href="tutorials.html">Dev Notes</a>
<a href="about.html">About</a>
<a href="assets.html">Assets</a>
</div>
</nav>
<div class="container">
<section class="section">
<div style="display: flex; align-items: center; gap: 1rem; margin-bottom: 0.5rem;">
<a href="projects.html" style="color: var(--text-muted); text-decoration: none; font-size: 1.2rem;"></a>
<h1 style="font-family: var(--font-display); color: #fff; margin: 0;">Scratch Extensions</h1>
</div>
<p style="color: var(--text-muted); font-size: 0.9rem; margin-bottom: 2.5rem;">
Eine Sammlung von mir erstellter Erweiterungen für TurboWarp, PenguinMod und Scratch Everywhere.
</p>
<div class="controls-container">
<input type="text" id="searchInput" class="search-input"
placeholder="Suche nach Extensions (z.B. Audio, Visuals)...">
<div class="filter-tags" id="filterTags">
<button class="filter-tag active" data-filter="all">Alle</button>
<button class="filter-tag" data-filter="TurboWarp">TurboWarp</button>
<button class="filter-tag" data-filter="PenguinMod">PenguinMod</button>
<button class="filter-tag" data-filter="SE!">SE!</button>
<button class="filter-tag" data-filter="Costume">Costume</button>
<button class="filter-tag" data-filter="Operator">Operator</button>
</div>
</div>
<div class="grid" id="extensionGrid">
<!-- Extensions will be injected here via JS -->
</div>
</section>
</div>
<div class="toast" id="toast">Link kopiert!</div>
<!-- SVG Icons -->
<svg style="display: none;">
<defs>
<symbol id="icon-copy" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
stroke-linecap="round" stroke-linejoin="round">
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
</symbol>
<symbol id="icon-docs" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
stroke-linecap="round" stroke-linejoin="round">
<path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"></path>
<path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"></path>
</symbol>
</defs>
</svg>
<script>
// --- Customise Extensions Here ---
const extensions = [
{
id: "scene-manager",
name: "Scene Manager",
description: "Ein Tool, um verschiedene Projekte als einzelne Szenen zu verwalten (gedacht für Scratch Everywhere!). ",
imagePath: "img/extensions/scene-manager.png",
tags: ["SE!"],
status: "new", // 'new', 'updated', or null
authors: "Br0tcraft",
link: "ext/scene-manager.js",
docs: "article.html?file=content/ext-scene-manager.md" // Optional documentation link
},
{
id: "penguin-physics",
name: "2D Physics Core",
description: "Eine Portierung von Box2D in Scratch via PenguinMod. Perfekt für Platformer und Puzzle.",
imageBg: "linear-gradient(135deg, #001a33, #0066cc)",
tags: ["PenguinMod", "Operator"],
status: null,
authors: null,
link: "https://example.com/ext/physics.js",
docs: "article.html?file=content/physics.md"
}
];
const grid = document.getElementById('extensionGrid');
const searchInput = document.getElementById('searchInput');
const filterButtons = document.querySelectorAll('.filter-tag');
let currentFilter = 'all';
function renderExtensions() {
grid.innerHTML = '';
const query = searchInput.value.toLowerCase();
const filtered = extensions.filter(ext => {
const matchesSearch = ext.name.toLowerCase().includes(query) || ext.description.toLowerCase().includes(query) || ext.tags.some(t => t.toLowerCase().includes(query));
const matchesFilter = currentFilter === 'all' || ext.tags.includes(currentFilter);
return matchesSearch && matchesFilter;
});
if (filtered.length === 0) {
grid.innerHTML = '<div class="empty-state">Keine Extensions gefunden.</div>';
return;
}
filtered.forEach(ext => {
let statusHtml = '';
if (ext.status === 'new') statusHtml = '<span class="ext-status ext-status-new">[NEW]</span>';
if (ext.status === 'updated') statusHtml = '<span class="ext-status ext-status-updated">[UPDATED]</span>';
const tagsHtml = ext.tags.map(t => `<span class="ext-tag">${t}</span>`).join('');
const authorHtml = ext.authors ? `<span class="ext-author">${ext.authors}</span>` : '';
let docsBtn = '';
if (ext.docs) {
docsBtn = `<a href="${ext.docs}" class="ext-btn btn-docs"><svg width="14" height="14"><use href="#icon-docs"></use></svg> Docs</a>`;
}
let mediaHtml = '';
if (ext.imagePath) {
mediaHtml = `<img src="${ext.imagePath}" style="width:100%; height:100%; object-fit: cover;" alt="${ext.name}">`;
} else {
mediaHtml = `<div style="width:100%; height:100%; background: ${ext.imageBg}; display: flex; align-items: center; justify-content: center;">
<span style="font-family: var(--font-code); color: rgba(255,255,255,0.2); font-size: 2rem;">{ }</span>
</div>`;
}
const card = document.createElement('article');
card.className = 'card';
card.innerHTML = `
<div style="height: 140px; overflow: hidden;">
${mediaHtml}
</div>
<div class="card-content">
<h3 class="card-title" style="margin-bottom: 0.2rem;">${ext.name}${statusHtml}</h3>
${authorHtml}
<div class="ext-tags">${tagsHtml}</div>
<p class="card-desc" style="margin-top: 1rem;">${ext.description}</p>
<div class="ext-actions">
<button class="ext-btn btn-copy" onclick="copyLink('${ext.link}')">
<svg width="14" height="14"><use href="#icon-copy"></use></svg> Link kopieren
</button>
${docsBtn}
</div>
</div>
`;
grid.appendChild(card);
});
}
// Events
searchInput.addEventListener('input', renderExtensions);
filterButtons.forEach(btn => {
btn.addEventListener('click', () => {
filterButtons.forEach(b => b.classList.remove('active'));
btn.classList.add('active');
currentFilter = btn.dataset.filter;
renderExtensions();
});
});
// Copy to clipboard
window.copyLink = function (link) {
navigator.clipboard.writeText(link).then(() => {
showToast("Link kopiert!");
}).catch(err => {
console.error("Copy failed", err);
showToast("Fehler beim Kopieren!");
});
};
function showToast(msg) {
const toast = document.getElementById('toast');
toast.innerText = msg;
toast.classList.add('show');
setTimeout(() => {
toast.classList.remove('show');
}, 2500);
}
// Initial render
renderExtensions();
</script>
</body>
</html>

View File

@@ -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.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.3 KiB

BIN
img/Apps/Zynk/Banner.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
img/Apps/Zynk/Logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

49
img/Apps/Zynk/Logo.svg Normal file
View File

@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="utf-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0.188 455.904 131.961" xmlns:bx="https://boxy-svg.com">
<defs>
<linearGradient x1="336.42148" y1="180.2661" x2="182.42881" y2="180.2661" gradientUnits="userSpaceOnUse" id="color-1">
<stop offset="0" stop-color="#019bc8"/>
<stop offset="1" stop-color="#3cd8e7"/>
</linearGradient>
<linearGradient x1="147.35625" y1="93.33003" x2="351.99218" y2="93.33003" gradientUnits="userSpaceOnUse" id="color-2">
<stop offset="0" stop-color="#1bc6dd"/>
<stop offset="0.4882" stop-color="#38d4e5"/>
<stop offset="0.99" stop-color="#3ed9e8"/>
</linearGradient>
<linearGradient x1="125.80128" y1="264.46344" x2="330.4372" y2="264.46344" gradientUnits="userSpaceOnUse" id="color-3">
<stop offset="0" stop-color="#1bc6dd"/>
<stop offset="0.4882" stop-color="#38d4e5"/>
<stop offset="0.99" stop-color="#3ed9e8"/>
</linearGradient>
<linearGradient x1="134.2437" y1="180.15041" x2="288.23637" y2="180.15041" gradientUnits="userSpaceOnUse" id="color-4">
<stop offset="0" stop-color="#019bc8"/>
<stop offset="1" stop-color="#3cd8e7"/>
</linearGradient>
<view id="view-1">
<title>View 1</title>
</view>
<style bx:fonts="Alan Sans">@import url(https://fonts.googleapis.com/css2?family=Alan+Sans%3Aital%2Cwght%400%2C300..900&amp;display=swap);</style>
<bx:export>
<bx:file format="png" href="#object-0" path="Unbetitelt.png"/>
<bx:file format="html" path="Unbetitelt.html" excluded="true"/>
<bx:file format="svg" path="Unbetitelt.svg" excluded="true"/>
<bx:file format="jpeg" path="Unbetitelt.jpeg" excluded="true"/>
<bx:file format="gif" path="Unbetitelt.gif" excluded="true"/>
<bx:file format="png" path="Logo.png" dpi="400" antialiasing="false"/>
</bx:export>
</defs>
<rect x="3.777" y="0.128" width="441.573" height="132.608" style="paint-order: fill; stroke-opacity: 0; stroke-width: 0px; stroke: rgb(2, 119, 151); fill: rgb(0, 119, 153);" id="object-0"/>
<g transform="matrix(0.522704, 0, 0, 0.61619, -60.055473, -45.021687)" style="">
<g stroke-width="0" stroke-miterlimit="10">
<path d="M138.17337,281.74035c-5.37056,0 -9.72425,-4.59731 -9.72425,-10.26836v-9.60405c0,-5.67106 4.3537,-10.26836 9.72425,-10.26836h185.18742c5.37056,0 9.72425,4.59731 9.72425,10.26836v9.60405c0,5.67106 -4.3537,10.26836 -9.72425,10.26836z" fill="#008ec0" stroke="#27c7d3"/>
<path d="M159.28705,110.60695c-5.37056,0 -9.72425,-4.59731 -9.72425,-10.26836v-9.60405c0,-5.67106 4.3537,-10.26836 9.72425,-10.26836h185.18742c5.37056,0 9.72425,4.59731 9.72425,10.26836v9.60405c0,5.67106 -4.3537,10.26836 -9.72425,10.26836z" fill="#008ec0" stroke="#27c7d3"/>
<path d="M142.66363,221.90843c23.25511,-19.01391 114.30793,-94.07711 114.30793,-94.07711l-7.14549,-6.85484l39.94877,0.55401l-9.89989,28.90364l-7.14549,-8.27088c0,0 -92.86371,78.88336 -115.21023,97.17507c-8.34511,6.83087 -16.77168,5.96334 -19.25286,2.92872c-2.57888,-3.15411 -5.46447,-12.29544 4.39726,-20.35862z" fill="#008ec0" stroke="none"/>
<path d="M331.81274,144.69689c-23.25511,19.01391 -114.30793,94.07711 -114.30793,94.07711l7.14549,6.85484l-39.94877,-0.55401l9.89989,-28.90364l7.14549,8.27088c0,0 92.86371,-78.88336 115.21023,-97.17507c8.34511,-6.83087 16.77168,-5.96334 19.25286,-2.92872c1.38768,1.69722 2.86418,5.12801 2.3956,9.19157c-0.40222,3.48818 -2.23769,7.44263 -6.79287,11.16705z" fill="#008ec0" stroke="none"/>
<path d="M329.54002,141.96962c-23.25511,19.01391 -114.30793,94.07711 -114.30793,94.07711l7.14549,6.85484l-39.94877,-0.55401l9.89989,-28.90364l7.14549,8.27088c0,0 92.86371,-78.88336 115.21023,-97.17507c8.34511,-6.83087 16.77168,-5.96334 19.25286,-2.92872c1.38768,1.69721 2.86418,5.12801 2.3956,9.19157c-0.40223,3.48818 -2.23769,7.44263 -6.79286,11.16705z" fill="url(#color-1)" stroke="none"/>
<path d="M157.08051,108.40042c-5.37056,0 -9.72425,-4.59731 -9.72425,-10.26836v-9.60405c0,-5.67106 4.3537,-10.26836 9.72425,-10.26836h185.18742c5.37056,0 9.72425,4.59731 9.72425,10.26836v9.60405c0,5.67106 -4.3537,10.26836 -9.72425,10.26836z" fill="url(#color-2)" stroke="#27c7d3"/>
<path d="M135.52553,279.53382c-5.37056,0 -9.72425,-4.59731 -9.72425,-10.26836v-9.60405c0,-5.67106 4.3537,-10.26836 9.72425,-10.26836h185.18742c5.37056,0 9.72425,4.59731 9.72425,10.26836v9.60405c0,5.67106 -4.3537,10.26836 -9.72425,10.26836z" fill="url(#color-3)" stroke="#27c7d3"/>
<path d="M141.12516,218.44689c23.25511,-19.01391 114.30793,-94.07711 114.30793,-94.07711l-7.14549,-6.85484l39.94877,0.55401l-9.89989,28.90364l-7.14549,-8.27088c0,0 -92.86371,78.88336 -115.21023,97.17507c-8.34511,6.83087 -16.77168,5.96334 -19.25286,2.92872c-2.57888,-3.15411 -5.46447,-12.29544 4.39726,-20.35862z" fill="url(#color-4)" stroke="none"/>
</g>
</g>
<text style="fill: rgb(61, 216, 232); font-family: &quot;Alan Sans&quot;; font-size: 28px; letter-spacing: -1.1px; white-space: pre;" transform="matrix(5.874275, 0, 0, 6.425704, -902.711975, -230.758133)" x="175.446" y="55.849" dx="-0.072" dy="-0.11">YNK</text>
</svg>

After

Width:  |  Height:  |  Size: 5.0 KiB

53
img/Apps/Zynk/Logo2.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 276 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 418 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

BIN
img/avatar.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 282 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

View File

@@ -1,99 +1,266 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html lang="de">
<head> <head>
<link rel="icon" href="img/favicon.jpg" type="image/png">
<link rel="stylesheet" href="style.css">
<meta charset="UTF-8"> <meta charset="UTF-8">
<title>BR0TCRAFT</title> <meta name="viewport" content="width=device-width, initial-scale=1.0">
</head> <title>BR0TCRAFT | Home</title>
<body> <link rel="stylesheet" href="style.css">
<div class="header"> <link
<h1>BR0TCRAFT</h1> href="https://fonts.googleapis.com/css2?family=Fira+Code:wght@400;600&family=Inter:wght@300;400;600;800&family=Orbitron:wght@700&display=swap"
</div> rel="stylesheet">
<div class="header-image"> <style>
<img src="img/Background3.jpg" alt="Header Image"> .hero-short {
</div> font-size: 1.15rem;
<div class="contact-icons"> color: var(--text-muted);
<a href="your discord link" target="_blank"><img src="img/contact/discord.png" alt="Discord"></a> line-height: 1.7;
<a href="mailto:info.br0tcraft.de"><img src="img/contact/email.png" alt="Email"></a> max-width: 560px;
<a href="https://github.com/Br0tcraft"><img src="img/contact/github.png" alt="Email"></a>
</div>
<div class="container">
<div class="section">
<div class="section-title">Apps</div>
<a href="apps/Br0tbot/index.html" class="item">
<div class="item-content">
<img src="img/Apps/TigerBr0tbot-large.png" alt="App 1 Image">
<div class="item-text">Br0tbot</div>
</div>
</a>
<a href="apps/Tiny-Snake/index.html" class="item">
<div class="item-content">
<img src="img/Apps/snake-large.png" alt="App 1 Image">
<div class="item-text">Tiny Snake</div>
</div>
</a>
<a href="apps/Br0tkabeln/index.html" class="item">
<div class="item-content">
<img src="img/Apps/Br0tkabelnlarge.png" alt="App 1 Image">
<div class="item-text">Br0tkabeln</div>
</div>
</a>
</div>
<div class="section">
<div class="section-title">Entwicklungsumgebungen</div>
<a href="/enviroments/PocketCode/index.html" class="item devvlog-item">
<div class="item-content">
<img src="img/developmentEnvironment/PocketCode.jpeg" alt="App 1 Image">
<div class="item-text">Pocket Code</div>
</div>
</a>
<a href="" class="item devvlog-item">
<div class="item-content">
<img src="img/Background3.jpg" alt="App 1 Image">
<div class="item-text">bald kommt mehr</div>
</div>
</a>
</div>
<div class="section">
<div class="section-title">Devvlogs</div>
<a href="apps/Tiny-Snake/index.html" class="item devvlog-item">
<div class="item-content">
<img src="img/Apps/Earthlarge.png" alt="App 1 Image">
<div class="item-text">The Simulation</div>
</div>
</a>
<a href="" class="item devvlog-item">
<div class="item-content">
<img src="img/Background3.jpg" alt="App 1 Image">
<div class="item-text">Bald kommt mehr</div>
</div>
</a>
</div>
<div class="section">
<div class="section-title">Anleitungen</div>
<a href="/" class="item anleitung-item">
<div class="item-content">
<img src="/img/developmentEnvironment/PocketCode.jpeg" alt="App 1 Image">
<div class="item-text">Erstes Handy game (Pocket Code) kommt bald</div>
</div>
</a>
<a href="/" class="item anleitung-item">
<div class="item-content">
<img src="/img/Background3.jpg" alt="App 1 Image">
<div class="item-text">mehr kommt bald ... irgendwann ... vielleicht ...</div>
</div>
</a>
</div>
</div>
<script>
document.addEventListener("visibilitychange", (event) => {
if (document.visibilityState == "visible") {
document.title = "BR0TCRAFT";
} else {
document.title = "Come back, please";
} }
});
</script> .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;
}
</style>
</head>
<body>
<nav class="navbar">
<div class="brand">BR0TCRAFT</div>
<div class="nav-links">
<a href="index.html" class="active">Home</a>
<a href="projects.html">Projects</a>
<a href="tutorials.html">Dev Notes</a>
<a href="about.html">About</a>
<a href="assets.html">Assets</a>
</div>
</nav>
<div class="container">
<!-- Hero -->
<section class="section hero">
<div class="hero-content">
<div class="text-col">
<div class="social-row">
<a href="https://discord.gg/deinlink" target="_blank" class="social-link" title="Discord">
<img src="https://cdn.simpleicons.org/discord/ff4d4d" alt="Discord">
</a>
<a href="mailto:info@br0tcraft.de" class="social-link" title="Email">
<img src="https://cdn.simpleicons.org/gmail/ff4d4d" alt="Email">
</a>
<a href="https://github.com/Br0tcraft" target="_blank" class="social-link" title="GitHub">
<img src="https://cdn.simpleicons.org/github/ff4d4d" alt="Github">
</a>
</div>
<h1><span style="color:var(--accent-primary)">Br0tcraft</span></h1>
<p class="hero-short">
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.
<br>
<strong>Work in progress, mit Liebe zum Detail.</strong>
</p>
<div class="tech-stack" style="margin-top: 1.25rem;">
<span class="tech-tag">C++</span>
<span class="tech-tag">C#</span>
<span class="tech-tag">GDScript</span>
<span class="tech-tag">Python</span>
<span class="tech-tag">JavaScript</span>
<span class="tech-tag">Dart</span>
<span class="tech-tag">Scratch</span>
</div>
<div style="margin-top: 1.25rem; display: flex; gap: 0.75rem; flex-wrap: wrap;">
<a href="projects.html" class="btn">Meine Projekte</a>
<a href="about.html" class="btn"
style="background: transparent; border: 1px solid #333; color: var(--text-muted);">Über mich</a>
</div>
</div>
<div class="avatar-wrapper">
<img src="img/avatar.png" alt="Br0tcraft Avatar" class="avatar">
</div>
</div>
</section>
<section class="section">
<h2 style="font-family: var(--font-display); margin-bottom: 0.5rem; color: #fff;">
Contributions &amp; Open Source
</h2>
<p style="color: var(--text-muted); margin-bottom: 2.5rem; max-width: 650px; font-size: 1.05rem;">
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.
</p>
<div class="contribution-grid">
<div class="contribution-card">
<div class="contribution-header">
<img src="https://github.com/ScratchEverywhere/ScratchEverywhere/blob/main/gfx/icon.png?raw=true"
alt="Scratch Everywhere" class="contribution-logo">
<div class="contribution-title-area">
<h3>Scratch Everywhere</h3>
<div class="contribution-links">
<a href="https://github.com/ScratchEverywhere/ScratchEverywhere" target="_blank"
class="contribution-link">
<img src="https://cdn.simpleicons.org/github/a0a0a0" alt="GitHub"> Code
</a>
<a href="https://github.com/ScratchEverywhere/ScratchEverywhere" target="_blank"
class="contribution-link">
<img src="https://cdn.simpleicons.org/planetscale/a0a0a0" alt="Web"> Website
</a>
</div>
</div>
</div>
<div class="contribution-body">
<p>
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.
</p>
<ul class="contribution-points">
<li>Arbeit am C++ Kern und der UI-Logik</li>
<li>Fixing von Bugs (die ich oft erst selbst eingebaut habe)</li>
<li>Alte Hardware mit modernem Code zum Leben erwecken</li>
</ul>
<div class="contribution-status">
<span class="status-tag">Aktiv dabei</span>
</div>
</div>
</div>
</div>
</section>
</div>
<script>
</script>
</body> </body>
</html> </html>

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="6.163" height="4.265" viewBox="0 0 6.163 4.265"><path fill="none" stroke="#000" stroke-linecap="round" stroke-miterlimit="10" stroke-width=".5" d="m.25.25 2.831 3.765m0 0L5.913.25"/></svg>

After

Width:  |  Height:  |  Size: 235 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="4.265" height="6.163" viewBox="0 0 4.265 6.163"><path fill="none" stroke="#000" stroke-linecap="round" stroke-miterlimit="10" stroke-width=".5" d="M4.015.25.25 3.081m0 0 3.765 2.832"/></svg>

After

Width:  |  Height:  |  Size: 237 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="4.265" height="6.163" viewBox="0 0 4.265 6.163"><path fill="none" stroke="#000" stroke-linecap="round" stroke-miterlimit="10" stroke-width=".5" d="m.25.25 3.765 2.831m0 0L.25 5.913"/></svg>

After

Width:  |  Height:  |  Size: 236 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="21.052" height="16.147" viewBox="0 0 21.052 16.147"><g fill="none" stroke="#000" stroke-miterlimit="10" stroke-width="1.5"><path d="M1.47 14.706a.72.72 0 0 1-.72-.72V1.47a.72.72 0 0 1 .72-.72h7.596c.577 0 .983.625 1.448.625.44 0 1.075-.625 1.613-.625h7.455a.72.72 0 0 1 .72.72v12.516a.72.72 0 0 1-.72.72H12.03c-.527 0-1.018.691-1.449.691-.456 0-.983-.69-1.547-.69z"/><path stroke-linecap="round" d="m10.416 1.433.098 13.561"/></g></svg>

After

Width:  |  Height:  |  Size: 483 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="5.339" height="5.067" viewBox="0 0 5.339 5.067"><g fill="none" stroke="#000" stroke-linecap="round" stroke-miterlimit="10" stroke-width=".5"><path d="M.25 4.817V2.56l.893-.438v-.624L2.626.25M2.12 2.024h.506"/><path d="M1.146 2.12v2.697H.219h2.494-.556s.043-.16.028-.504c-.013-.31.098-.548.443-.575M2.713.25l1.483 1.247v.624l.893.438v2.258M2.644 1.59v1.062m.488-.628h-.506"/><path d="M4.194 2.12v2.697h.927-2.495.557s-.044-.16-.029-.504c.014-.31-.098-.548-.442-.575"/></g></svg>

After

Width:  |  Height:  |  Size: 524 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="8.138" height="8.863" viewBox="0 0 8.138 8.863"><g fill="none" stroke="#000" stroke-miterlimit="10" stroke-width=".5"><path d="M1.268 8.6C.715 8.6.208 8.017.253 7.524l.015-2.007c0-.071-.06 1.188-.006 2.014.028.288.163 1.074 1.036 1.055.93-.02 3.957.057 5.584.026.863-.016.99-.772 1-1.023.033-.794.006-2.148.006-2.072v2.081c0 .553-.447 1.015-.985 1z"/><path stroke-linecap="round" stroke-linejoin="round" d="M4.062.25v6.477L.475 2.877m7.188 0-3.6 3.85V.25"/></g></svg>

After

Width:  |  Height:  |  Size: 514 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="9.444" height="12.174" viewBox="0 0 9.444 12.174"><g fill="none" stroke="#000" stroke-miterlimit="10" stroke-width=".5"><path d="M1.415 11.924c-.643 0-1.165-.448-1.165-1V1.25c0-.552.522-1 1.165-1h5.474l2.305 2.266v8.408c0 .552-.522 1-1.165 1z"/><path stroke-linecap="round" d="M8.977 2.482H6.89V.284M2.143 6.087H7.3M2.143 3.892H7.3M2.143 1.697h3.363M2.143 8.282H7.3m-5.157 2.195H7.3"/></g></svg>

After

Width:  |  Height:  |  Size: 442 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="21.951" height="21.08" viewBox="0 0 21.951 21.08"><g fill="none" stroke="#000" stroke-miterlimit="10" stroke-width="1.5"><path d="M.75 3.19A2.44 2.44 0 0 1 3.188.753c1.346 0 12.76.065 12.76.065S19.135.74 18.99 3.821c-.016.35.005 10.811.005 10.811s.606.062 1.466.014c1.453-.081 1.49 1.304 1.49 1.304s.05.572.038 1.214c-.054 2.806-2.626 3.165-2.626 3.165s-10.042-.132-11.893-.09c-2.904.066-2.93-2.738-2.93-2.738L4.486 7.185s-1.174-.021-1.298-.021c-1.181 0-2.211-.591-2.435-1.706C.72 5.302.75 3.355.75 3.19z"/><path stroke-linecap="round" d="M2.204.93c2.225-.94 2.249 2.106 2.249 2.106l.044 4.003m3.856-1.328h6.718m-6.718 4.688h6.718m4.037 4.201s-8.07.071-9.791.062c-.84-.004 1.256 5.853-2.865 5.407"/></g></svg>

After

Width:  |  Height:  |  Size: 756 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="9.952" height="9.714" viewBox="0 0 9.952 9.714"><g fill="none" stroke="#000" stroke-miterlimit="10" stroke-width=".5"><path stroke-linecap="round" stroke-linejoin="round" d="M3.73 1.824V.25h2.492v1.481m-.007.125L7.369.702l1.826 1.827L8.11 3.614m.019-.005h1.573V6.1h-1.48M1.843 3.614.757 2.53 2.584.702l1.154 1.154M1.731 6.1H.25V3.61h1.574M6.23 7.983v1.481H3.737V7.891M8.117 6.1l1.085 1.086-1.826 1.826L6.222 7.86M3.73 7.859 2.577 9.012.75 7.186 1.836 6.1"/><path d="M3.404 4.854a1.572 1.572 0 1 1 3.145 0 1.572 1.572 0 0 1-3.145 0z"/></g></svg>

After

Width:  |  Height:  |  Size: 591 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="15.471" height="18.719" viewBox="0 0 15.471 18.719"><g fill="none" stroke="#000" stroke-linecap="round" stroke-miterlimit="10"><path stroke-linejoin="round" d="m4.09 8.1-.954 6.067L.5 8.348l1.94-6.181L5.257.5 6.56 2.59h1.818"/><path d="M8.742 18.22H3.106v-4.185"/><path d="M12.316 14.035v4.184H6.679"/><path stroke-linejoin="round" d="M7.092 2.59H8.91L10.213.5l2.818 1.667 1.94 6.181-2.637 5.819-.953-6.066"/></g></svg>

After

Width:  |  Height:  |  Size: 466 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="17.973" height="18.286" viewBox="0 0 17.973 18.286"><g fill="none" stroke="#000" stroke-miterlimit="10" stroke-width="1.5"><path d="M5.795 9.1a3.192 3.192 0 1 1 6.384 0 3.192 3.192 0 0 1-6.384 0z"/><path stroke-linecap="round" d="M8.987.75v3.17m8.236 5.18h-3.17m1.126-6.191L12.938 5.15m2.241 10.143-2.241-2.241M3.92 8.922H.75m4.286-3.951-2.242-2.24m2.242 10.142-2.242 2.242m6.193 2.421v-3.17"/></g></svg>

After

Width:  |  Height:  |  Size: 451 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="8.296" height="8.83" viewBox="0 0 8.296 8.83"><path fill="none" stroke="#000" stroke-miterlimit="10" stroke-width=".5" d="M.25 8.58v-.914a2 2 0 0 1 2-2h3.796a2 2 0 0 1 2 2v.914zm1.62-6.053a2.277 2.277 0 1 1 4.555 0 2.277 2.277 0 0 1-4.554 0z"/></svg>

After

Width:  |  Height:  |  Size: 297 B

1
mini/assets/icons/x.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="7.623" height="7.623" viewBox="0 0 7.623 7.623"><path fill="none" stroke="#000" stroke-linecap="round" stroke-miterlimit="10" stroke-width=".5" d="m.25.25 7.123 7.123m-7.123 0L7.373.25"/></svg>

After

Width:  |  Height:  |  Size: 240 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="64.338" height="41.66" viewBox="0 0 64.338 41.66"><g stroke-miterlimit="10"><path fill="#faeccb" d="M.283 41.35V.37h63.783v40.98z"/><path fill="none" stroke="#000" stroke-linecap="round" stroke-width=".5" d="M32.194 29.421h-4.238c-3.81 0-17.322-3.704-23.964-3.704-2.272 0-3.74.146-3.74.146V5.05H15.83V.257h16.339"/><path fill="none" stroke="#000" stroke-linecap="round" stroke-width=".5" d="M32.168.257h16.34V5.05h15.576v20.813s-1.467-.146-3.74-.146c-6.641 0-20.154 3.704-23.964 3.704h-4.237"/><path fill="#fff" stroke="#000" stroke-width=".5" d="M25.76 24.947a1.5 1.5 0 0 1-1.5-1.5v-4.189a1.5 1.5 0 0 1 1.5-1.5h12.685a1.5 1.5 0 0 1 1.5 1.5v4.19a1.5 1.5 0 0 1-1.5 1.5z"/><path fill="#d86161" stroke="#000" stroke-width=".5" d="M17.109 23.66a1.237 1.237 0 0 1-1.238-1.238v-2.138c0-.684.554-1.238 1.238-1.238h2.138c.683 0 1.237.554 1.237 1.238v2.138c0 .683-.554 1.237-1.237 1.237z"/><path fill="none" stroke="#000" stroke-width=".5" d="M21.965 19.827a.69.69 0 1 1 1.38 0 .69.69 0 0 1-1.38 0zm0 3.052a.69.69 0 1 1 1.38 0 .69.69 0 0 1-1.38 0z"/><path fill="#ffcd48" stroke="#000" stroke-width=".5" d="M21.965 19.827a.69.69 0 1 1 1.38 0 .69.69 0 0 1-1.38 0zm0 3.052a.69.69 0 1 1 1.38 0 .69.69 0 0 1-1.38 0zm19.108-3.052a.69.69 0 1 1 1.38 0 .69.69 0 0 1-1.38 0zm0 3.052a.69.69 0 1 1 1.38 0 .69.69 0 0 1-1.38 0z"/><path fill="none" stroke="#000" stroke-linecap="round" stroke-width=".5" d="M32.194 33.125h-4.238c-3.81 0-17.322-3.704-23.964-3.704-2.272 0-3.74.145-3.74.145v-5.01"/><path fill="none" stroke="#000" stroke-linecap="round" stroke-width=".5" d="M64.084 24.549v5.01s-1.467-.146-3.74-.146c-6.641 0-20.154 3.704-23.964 3.704h-4.237"/><path fill="#bc2d2d" stroke="#000" stroke-width=".5" d="M18.127 30.68a.426.426 0 0 1-.328-.506l.257-1.207a.426.426 0 0 1 .505-.328l6.07 1.29c.23.05.377.275.328.505l-.256 1.207a.426.426 0 0 1-.505.328zm20.496 1.436a.426.426 0 0 1-.505-.329l-.256-1.206a.426.426 0 0 1 .328-.505l6.07-1.29a.426.426 0 0 1 .505.328l.257 1.207a.426.426 0 0 1-.328.505z"/><path fill="#fff" stroke="#000" stroke-width=".5" d="M47.58 19.551c-.463-.47-.52-1.165-.126-1.553l1.23-1.214c.394-.388 1.089-.321 1.552.149l1.45 1.47c.463.47.52 1.165.126 1.553L50.58 21.17c-.394.388-1.088.322-1.552-.148zM4.134 17.685a.597.597 0 0 1-.844 0l-2.165-2.166a.597.597 0 0 1 0-.843L9.839 5.96a.597.597 0 0 1 .844 0l2.165 2.165a.597.597 0 0 1 0 .844z"/><path fill="#bc2d2d" stroke="#000" stroke-width=".5" d="M6.082 21.666a.5.5 0 0 1-.708 0L3.61 19.902a.5.5 0 0 1 0-.707l1.764-1.765a.5.5 0 0 1 .708 0l1.764 1.765a.5.5 0 0 1 0 .707zm2.97-2.97a.5.5 0 0 1-.707 0L6.58 16.93a.5.5 0 0 1 0-.707l1.765-1.765a.5.5 0 0 1 .707 0l1.765 1.765a.5.5 0 0 1 0 .707zm2.97-2.971a.5.5 0 0 1-.707 0l-1.764-1.764a.5.5 0 0 1 0-.707l1.764-1.765a.5.5 0 0 1 .707 0l1.765 1.765a.5.5 0 0 1 0 .707z"/><path fill="#fff" stroke="#000" stroke-width=".5" d="M51.567 9.007a.597.597 0 0 1 0-.844l2.165-2.165a.597.597 0 0 1 .844 0l8.714 8.714a.597.597 0 0 1 0 .844l-2.165 2.165a.597.597 0 0 1-.844 0z"/><path fill="#bc2d2d" stroke="#000" stroke-width=".5" d="M14.993 12.755a.5.5 0 0 1-.707 0L12.52 10.99a.5.5 0 0 1 0-.707l1.765-1.764a.5.5 0 0 1 .707 0l1.764 1.764a.5.5 0 0 1 0 .707zm36.011 1.085a.5.5 0 0 1-.707 0l-1.765-1.764a.5.5 0 0 1 0-.707l1.765-1.764a.5.5 0 0 1 .707 0l1.765 1.764a.5.5 0 0 1 0 .707zm6.758 6.684a.5.5 0 0 1-.707 0L55.29 18.76a.5.5 0 0 1 0-.707l1.765-1.765a.5.5 0 0 1 .707 0l1.764 1.765a.5.5 0 0 1 0 .707zm-3.615-2.456a.64.64 0 0 1-.904 0l-2.255-2.255a.64.64 0 0 1 0-.903l2.255-2.255c.25-.25.654-.25.904 0l2.255 2.255c.25.25.25.654 0 .903z"/><path fill="#fff" stroke="#000" stroke-width=".5" d="M20.507 4.86a.5.5 0 0 1-.5-.5V.29L44.233.252V4.36a.5.5 0 0 1-.5.5z"/><path fill="none" stroke="#000" stroke-linecap="round" stroke-width=".5" d="M32.856 41.41H.25V29.428"/><path fill="none" stroke="#000" stroke-linecap="round" stroke-width=".5" d="M64.071 29.417v11.982H31.465"/><path stroke="#000" stroke-width=".5" d="M.253 5.033V.25h15.602v4.783zm48.232.007V.256h15.603V5.04z"/><path fill="#ffcd48" stroke="#d8ca2d" stroke-width=".5" d="M19.16 26.924a.277.277 0 1 1 .553 0 .277.277 0 0 1-.553 0zm.955.272a.277.277 0 1 1 .553 0 .277.277 0 0 1-.553 0zm-.698-1.182a.277.277 0 1 1 .554 0 .277.277 0 0 1-.554 0zm.983.271a.277.277 0 1 1 .554 0 .277.277 0 0 1-.553 0zm23.224 1.245a.277.277 0 1 1 0-.554.277.277 0 0 1 0 .553zm-.954.27a.277.277 0 1 1 0-.553.277.277 0 0 1 0 .554zm.697-1.181a.277.277 0 1 1 0-.554.277.277 0 0 1 0 .554zm-.984.271a.277.277 0 1 1 0-.554.277.277 0 0 1 0 .554z"/><path fill="#ffcd48" stroke="#000" stroke-width=".5" d="M4.487 14.548a.69.69 0 1 1 1.38 0 .69.69 0 0 1-1.38 0zM9.008 10a.69.69 0 1 1 1.38 0 .69.69 0 0 1-1.38 0z"/></g></svg>

After

Width:  |  Height:  |  Size: 4.6 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="64.327" height="41.52" viewBox="0 0 64.327 41.52"><g stroke="#000" stroke-miterlimit="10"><path fill="#b38969" stroke-width=".25" d="M.97 36.476a2.413 2.413 0 1 1 4.825 0 2.413 2.413 0 0 1-4.826 0z"/><path fill="#b38969" stroke-width=".25" d="M.97 31.65a2.413 2.413 0 1 1 4.825 0 2.413 2.413 0 0 1-4.826 0z"/><path fill="none" stroke-width=".25" d="M1.53 36.618a.17.17 0 0 1-.173-.172v-.224c0-.095.077-.172.172-.172h3.706c.095 0 .172.077.172.172v.224a.17.17 0 0 1-.172.172z"/><path fill="none" stroke-width=".25" d="M5.351 36.734c0 .734-.881 1.328-1.969 1.328-1.087 0-1.969-.594-1.969-1.328 0-.043.01-.128.01-.128l3.92.003s.008.083.008.125z"/><path fill="none" stroke-linecap="round" stroke-width=".25" d="m2.086 36.665 1.238 1.379m-.568-1.379 1.181 1.315m-.511-1.315 1.036 1.154m.448-.249-.814-.905m1.14.523-.47-.523M2.525 37.9l-1.109-1.235"/><path fill="none" stroke-width=".25" d="M1.53 31.792a.17.17 0 0 1-.173-.171v-.224c0-.095.077-.172.172-.172h3.706c.095 0 .172.077.172.172v.224a.17.17 0 0 1-.172.171z"/><path fill="none" stroke-width=".25" d="M5.351 31.908c0 .734-.881 1.329-1.969 1.329-1.087 0-1.969-.595-1.969-1.329 0-.043.01-.127.01-.127l3.92.002s.008.083.008.125z"/><path fill="none" stroke-linecap="round" stroke-width=".25" d="m2.086 31.84 1.238 1.378m-.568-1.378 1.181 1.314m-.511-1.314 1.036 1.153m.448-.248-.814-.905m1.14.522-.47-.522m-2.241 1.234L1.416 31.84"/><path fill="#cede59" stroke-width=".25" d="M5.795 31.65a2.413 2.413 0 1 1 4.826 0 2.413 2.413 0 0 1-4.826 0z"/><path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width=".25" d="M8.593 29.977s-.116.282.038.544c.166.282.574.563.739.949.06.141.16.573.054.907-.023.074-.124.406-.388.61s-.691.281-.866.276a1.5 1.5 0 0 1-.739-.256 1.2 1.2 0 0 1-.409-.546c-.103-.315-.074-.713.042-1.071.155-.477.43-.878.43-.878s.011.163.073.339c.081.232.213.343.25.402.03.046.103-.565.324-.858.208-.276.452-.418.452-.418"/><path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width=".25" d="M8.517 32.153a.55.55 0 0 1 .192.388c0 .201-.164.49-.504.486-.323-.005-.494-.314-.486-.495a1 1 0 0 1 .214-.526c.148-.18.32-.272.32-.272a1 1 0 0 0 .264.42"/><path fill="#cede59" stroke-width=".25" d="M5.795 36.476a2.413 2.413 0 1 1 4.826 0 2.413 2.413 0 0 1-4.826 0z"/><path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width=".25" d="M8.593 34.803s-.116.281.038.544c.166.282.574.563.739.948.06.142.16.574.054.908-.023.074-.124.405-.388.61s-.691.28-.866.276a1.5 1.5 0 0 1-.739-.257 1.2 1.2 0 0 1-.409-.546c-.103-.314-.074-.713.042-1.07.155-.477.43-.878.43-.878s.011.162.073.339c.081.232.213.343.25.402.03.046.103-.566.324-.858.208-.276.452-.418.452-.418"/><path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width=".25" d="M8.517 36.979a.55.55 0 0 1 .192.388c0 .2-.164.49-.504.485-.323-.004-.494-.314-.486-.495a1 1 0 0 1 .214-.525c.148-.18.32-.272.32-.272a1 1 0 0 0 .264.419"/><path fill="none" stroke-width=".25" d="M.97 31.65a2.413 2.413 0 1 1 4.825 0 2.413 2.413 0 0 1-4.826 0z"/><path fill="none" stroke-width=".25" d="M.97 36.476a2.413 2.413 0 1 1 4.825 0 2.413 2.413 0 0 1-4.826 0z"/><path fill="#5997de" stroke-width=".25" d="M10.62 31.65a2.413 2.413 0 1 1 4.826 0 2.413 2.413 0 0 1-4.825 0z"/><path fill="none" stroke-linejoin="round" stroke-width=".25" d="M11.196 31.87h3.675s-.823 1.28-1.837 1.28-1.838-1.28-1.838-1.28z"/><path fill="none" stroke-width=".25" d="M11.854 31.33a1.18 1.18 0 0 1 2.359 0c0 .185-.119.516-.119.516l-2.108.027s-.132-.347-.132-.543z"/><path fill="#5997de" stroke-width=".25" d="M10.62 36.476a2.413 2.413 0 1 1 4.826 0 2.413 2.413 0 0 1-4.825 0z"/><path fill="none" stroke-linejoin="round" stroke-width=".25" d="M11.196 36.695h3.675s-.823 1.28-1.837 1.28-1.838-1.28-1.838-1.28z"/><path fill="none" stroke-width=".25" d="M11.854 36.156a1.18 1.18 0 0 1 2.359 0c0 .185-.119.516-.119.516l-2.108.027s-.132-.347-.132-.543z"/><path fill="none" stroke-width=".5" d="M.25 41.27V.25h63.827v41.02z"/></g></svg>

After

Width:  |  Height:  |  Size: 3.9 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 4.9 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="64.327" height="41.52" viewBox="0 0 64.327 41.52"><g stroke="#000" stroke-miterlimit="10"><path fill="#b38969" stroke-width=".25" d="M33.184 36.008a2.413 2.413 0 1 1 4.825 0 2.413 2.413 0 0 1-4.825 0z"/><path fill="none" stroke-width=".25" d="M33.743 36.15a.17.17 0 0 1-.171-.172v-.224c0-.095.077-.172.171-.172h3.707c.094 0 .171.077.171.172v.224a.17.17 0 0 1-.171.171z"/><path fill="none" stroke-width=".25" d="M37.565 36.266c0 .733-.881 1.328-1.968 1.328-1.088 0-1.97-.595-1.97-1.328 0-.044.01-.128.01-.128l3.92.002s.008.083.008.126z"/><path fill="none" stroke-linecap="round" stroke-width=".25" d="m34.3 36.197 1.239 1.378m-.569-1.378 1.181 1.314m-.511-1.314 1.037 1.153m.447-.248-.813-.905m1.139.523-.47-.523m-2.24 1.235-1.11-1.235"/><path fill="#b38969" stroke-width=".25" d="M23.864 35.69a2.413 2.413 0 1 1 4.826 0 2.413 2.413 0 0 1-4.826 0z"/><path fill="none" stroke-width=".25" d="M24.424 35.832a.17.17 0 0 1-.172-.171v-.225c0-.094.077-.171.172-.171h3.706c.095 0 .172.077.172.171v.225a.17.17 0 0 1-.172.171z"/><path fill="none" stroke-width=".25" d="M28.246 35.948c0 .734-.882 1.329-1.97 1.329-1.087 0-1.968-.595-1.968-1.329 0-.043.009-.127.009-.127l3.92.002s.009.083.009.125z"/><path fill="none" stroke-linecap="round" stroke-width=".25" d="m24.98 35.88 1.239 1.378m-.569-1.378 1.182 1.314m-.512-1.314 1.037 1.153m.447-.248-.813-.905m1.139.522-.469-.522m-2.241 1.234-1.11-1.234"/><path fill="#cede59" stroke-width=".25" d="M38.01 35.222a2.413 2.413 0 1 1 4.825 0 2.413 2.413 0 0 1-4.826 0z"/><path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width=".25" d="M40.808 33.549s-.117.281.037.543c.166.283.574.563.739.949.06.142.16.573.054.908-.023.074-.123.405-.388.61-.263.204-.69.28-.866.276a1.5 1.5 0 0 1-.738-.257 1.2 1.2 0 0 1-.41-.546c-.102-.314-.074-.713.042-1.07.155-.477.43-.878.43-.878s.012.162.073.338c.082.233.213.344.25.402.03.047.103-.565.324-.857.208-.276.453-.418.453-.418"/><path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width=".25" d="M40.731 35.725a.55.55 0 0 1 .192.388c0 .2-.164.49-.504.485-.322-.005-.494-.314-.485-.495a1 1 0 0 1 .213-.526c.148-.18.32-.271.32-.271a1 1 0 0 0 .264.419"/><path fill="#cede59" stroke-width=".25" d="M19.038 34.69a2.413 2.413 0 1 1 4.826 0 2.413 2.413 0 0 1-4.826 0z"/><path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width=".25" d="M21.837 33.017s-.117.282.037.544c.166.282.574.563.739.948.06.142.16.574.054.908-.023.074-.123.406-.388.61-.263.204-.69.281-.866.276a1.5 1.5 0 0 1-.738-.256 1.2 1.2 0 0 1-.41-.547c-.102-.314-.074-.712.042-1.07.155-.477.43-.878.43-.878s.012.163.073.339c.082.232.213.343.25.402.03.046.103-.565.324-.858.208-.276.453-.418.453-.418"/><path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width=".25" d="M21.76 35.193a.55.55 0 0 1 .192.388c0 .201-.164.49-.504.486-.322-.005-.494-.315-.485-.495a1 1 0 0 1 .213-.526c.148-.18.32-.272.32-.272a1 1 0 0 0 .264.42"/><path fill="none" stroke-width=".25" d="M33.184 36.008a2.413 2.413 0 1 1 4.825 0 2.413 2.413 0 0 1-4.825 0zm-9.32-.318a2.413 2.413 0 1 1 4.826 0 2.413 2.413 0 0 1-4.826 0z"/><path fill="#5997de" stroke-width=".25" d="M14.213 33.508a2.413 2.413 0 1 1 4.825 0 2.413 2.413 0 0 1-4.825 0z"/><path fill="none" stroke-linejoin="round" stroke-width=".25" d="M14.788 33.726h3.675s-.823 1.281-1.837 1.281-1.838-1.28-1.838-1.28z"/><path fill="none" stroke-width=".25" d="M15.446 33.187a1.18 1.18 0 0 1 2.359 0c0 .185-.119.516-.119.516l-2.108.027s-.132-.347-.132-.543z"/><path fill="#5997de" stroke-width=".25" d="M42.835 33.72a2.413 2.413 0 1 1 4.826 0 2.413 2.413 0 0 1-4.826 0z"/><path fill="none" stroke-linejoin="round" stroke-width=".25" d="M43.41 33.939h3.675s-.823 1.28-1.837 1.28-1.837-1.28-1.837-1.28z"/><path fill="none" stroke-width=".25" d="M44.069 33.4a1.18 1.18 0 0 1 2.358 0c0 .185-.118.515-.118.515l-2.108.027s-.132-.347-.132-.542z"/><path fill="none" stroke-width=".5" d="M.25 41.27V.25h63.827v41.02z"/></g></svg>

After

Width:  |  Height:  |  Size: 3.9 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="64.327" height="41.52" viewBox="0 0 64.327 41.52"><g stroke="#000" stroke-miterlimit="10"><path fill="#b38969" stroke-width=".25" d="M55.12 18.47a2.413 2.413 0 1 1 4.825 0 2.413 2.413 0 0 1-4.825 0z"/><path fill="none" stroke-width=".25" d="M55.68 18.612a.17.17 0 0 1-.172-.172v-.224c0-.095.077-.172.172-.172h3.706c.095 0 .171.077.171.172v.224a.17.17 0 0 1-.171.172z"/><path fill="none" stroke-width=".25" d="M59.502 18.728c0 .734-.882 1.328-1.97 1.328-1.087 0-1.968-.594-1.968-1.328 0-.043.009-.128.009-.128l3.92.003s.009.083.009.125z"/><path fill="none" stroke-linecap="round" stroke-width=".25" d="m56.237 18.66 1.238 1.378m-.568-1.378 1.18 1.314m.526-.162-1.036-1.153m1.483.906-.813-.906m1.139.523-.47-.523m-2.24 1.235-1.11-1.235"/><path fill="#b38969" stroke-width=".25" d="M47.605 11.386a2.413 2.413 0 1 1 4.825 0 2.413 2.413 0 0 1-4.825 0z"/><path fill="none" stroke-width=".25" d="M48.164 11.528a.17.17 0 0 1-.171-.172v-.224c0-.095.076-.172.171-.172h3.707c.094 0 .171.077.171.172v.224a.17.17 0 0 1-.171.172z"/><path fill="none" stroke-width=".25" d="M51.986 11.644c0 .733-.881 1.328-1.969 1.328-1.087 0-1.968-.595-1.968-1.328 0-.043.008-.128.008-.128l3.92.002s.01.084.01.126z"/><path fill="none" stroke-linecap="round" stroke-width=".25" d="m49.96 12.953-1.239-1.378m.67 0 1.181 1.315m.525-.162-1.035-1.153m1.483.905-.813-.905m1.138.522-.468-.522M49.16 12.81l-1.109-1.235"/><path fill="#cede59" stroke-width=".25" d="M13.31 9.902a2.413 2.413 0 1 1 4.826 0 2.413 2.413 0 0 1-4.826 0z"/><path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width=".25" d="M16.109 8.23s-.117.28.037.543c.166.282.574.563.739.948.06.142.16.574.054.908-.023.074-.124.406-.388.61s-.691.281-.866.276a1.5 1.5 0 0 1-.739-.256 1.2 1.2 0 0 1-.409-.547c-.103-.314-.074-.712.042-1.07.155-.477.43-.878.43-.878s.011.163.073.339c.082.232.213.343.25.402.03.046.103-.565.324-.858.208-.276.453-.418.453-.418"/><path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width=".25" d="M16.032 10.405a.55.55 0 0 1 .192.388c0 .2-.164.49-.504.485-.322-.004-.494-.314-.486-.494a1 1 0 0 1 .214-.526c.148-.18.32-.272.32-.272a1 1 0 0 0 .264.42"/><path fill="#cede59" stroke-width=".25" d="M2.629 20.198a2.413 2.413 0 1 1 4.825 0 2.413 2.413 0 0 1-4.825 0z"/><path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width=".25" d="M5.427 18.525s-.117.281.037.543c.166.283.575.563.74.949.06.142.159.573.053.908-.023.074-.123.405-.388.61-.263.204-.69.28-.866.276a1.5 1.5 0 0 1-.738-.257 1.2 1.2 0 0 1-.41-.546c-.102-.314-.074-.713.043-1.07.154-.477.43-.878.43-.878s.01.162.073.338c.081.233.212.344.25.402.029.047.103-.565.323-.857.209-.276.453-.418.453-.418"/><path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width=".25" d="M5.35 20.7a.55.55 0 0 1 .192.389c0 .2-.164.49-.504.485-.322-.005-.494-.314-.485-.495a1 1 0 0 1 .214-.526c.147-.18.32-.271.32-.271a1 1 0 0 0 .264.419"/><path fill="none" stroke-width=".25" d="M55.12 18.47a2.413 2.413 0 1 1 4.825 0 2.413 2.413 0 0 1-4.825 0zm-7.515-7.084a2.413 2.413 0 1 1 4.825 0 2.413 2.413 0 0 1-4.825 0z"/><path fill="#5997de" stroke-width=".25" d="M6.089 16.79a2.413 2.413 0 1 1 4.825 0 2.413 2.413 0 0 1-4.825 0z"/><path fill="none" stroke-linejoin="round" stroke-width=".25" d="M6.664 17.008h3.675s-.823 1.28-1.838 1.28c-1.014 0-1.837-1.28-1.837-1.28z"/><path fill="none" stroke-width=".25" d="M7.322 16.47a1.18 1.18 0 0 1 2.359 0c0 .184-.119.515-.119.515l-2.108.027s-.132-.347-.132-.543z"/><path fill="#5997de" stroke-width=".25" d="M9.688 13.238a2.413 2.413 0 1 1 4.826 0 2.413 2.413 0 0 1-4.826 0z"/><path fill="none" stroke-linejoin="round" stroke-width=".25" d="M10.264 13.457h3.674s-.822 1.28-1.837 1.28-1.837-1.28-1.837-1.28z"/><path fill="none" stroke-width=".25" d="M10.922 12.918a1.18 1.18 0 0 1 2.358 0c0 .185-.118.516-.118.516l-2.108.027s-.132-.347-.132-.543z"/><path fill="none" stroke-width=".5" d="M.25 41.27V.25h63.827v41.02z"/></g></svg>

After

Width:  |  Height:  |  Size: 3.9 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 4.9 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="12.295" height="6.36" viewBox="0 0 12.295 6.36"><g fill="none" stroke="#000" stroke-miterlimit="10" stroke-width=".5"><path d="M.75 1.903a.5.5 0 0 1-.5-.5V.75a.5.5 0 0 1 .5-.5h10.795a.5.5 0 0 1 .5.5v.653a.5.5 0 0 1-.5.5z"/><path d="M11.882 2.24c0 2.138-2.568 3.87-5.735 3.87S.413 4.378.413 2.24c0-.125.026-.371.026-.371l11.418.007s.025.242.025.365z"/><path stroke-linecap="round" d="M5.978 6.055 2.372 2.041M7.763 5.87 4.324 2.04M9.293 5.4 6.276 2.04m4.32 2.638L8.227 2.041m3.319 1.521L10.179 2.04M3.651 5.637.421 2.041"/></g></svg>

After

Width:  |  Height:  |  Size: 579 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="8.88" height="7.339" viewBox="0 0 8.88 7.339"><g fill="none" stroke="#000" stroke-miterlimit="10" stroke-width=".5"><path stroke-linejoin="round" d="M.25 4.169h8.38s-1.876 2.92-4.19 2.92S.25 4.17.25 4.17z"/><path d="M1.75 2.94a2.69 2.69 0 0 1 5.38 0c0 .421-.27 1.175-.27 1.175l-4.808.062S1.75 3.386 1.75 2.94z"/></g></svg>

After

Width:  |  Height:  |  Size: 369 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="8.235" height="10.812" viewBox="0 0 8.235 10.812"><g fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" stroke-width=".5"><path d="M5.38.25s-.768.445-1.42 1.311c-.693.919-.926 2.837-1.017 2.692-.117-.183-.529-.533-.784-1.261-.194-.554-.228-1.063-.228-1.063S1.065 3.188.58 4.683C.215 5.806.126 7.056.449 8.043.586 8.46 1 9.206 1.732 9.757c.32.241 1.327.778 2.318.805.55.015 1.891-.226 2.718-.866.83-.642 1.144-1.683 1.217-1.914.332-1.05.02-2.404-.17-2.85-.516-1.21-1.798-2.09-2.318-2.976C5.013 1.133 5.38.25 5.38.25"/><path d="M4.311 5.764s-.54.287-1.004.852c-.419.511-.642 1.17-.67 1.65-.027.568.511 1.539 1.523 1.553 1.066.015 1.583-.893 1.581-1.523-.02-.375-.146-.785-.601-1.217a3.13 3.13 0 0 1-.829-1.315"/></g></svg>

After

Width:  |  Height:  |  Size: 812 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="5.89" height="8.112" viewBox="0 0 5.89 8.112"><g fill="none" stroke="#000" stroke-miterlimit="10" stroke-width=".5"><path d="M5.454 3.707c0 1.37-1.123 2.482-2.509 2.482S.435 5.078.435 3.707c0-1.37 1.124-2.481 2.51-2.481s2.51 1.11 2.51 2.481z"/><path d="M1.867 1.43V.75a.5.5 0 0 1 .5-.5h1.156a.5.5 0 0 1 .5.5v.662c0 .046-.545-.263-1.023-.257-.589.008-1.133.321-1.133.275zM5.64 7.862c0 .23-5.39.077-5.39 0 0-.627.831-1.154 1.965-1.316.232-.033.496-.33.749-.33.29 0 .55.303.812.346 1.082.177 1.864.692 1.864 1.3z"/><path stroke-linecap="round" d="M.547 3.707h4.796"/></g></svg>

After

Width:  |  Height:  |  Size: 621 B

View File

@@ -0,0 +1,11 @@
<p>
Der <a href="https://de.wikipedia.org/wiki/Ambo" title="Ambo auf Wikipedia">Ambo</a> ist das feststehende, leicht
erhöhte Lesepult in der Kirche.
</p>
<p>
An dem Ambo findet das Lektorat statt, dies sind die Lesungen aus der Bibel und die
Fürbitten.
</p>
<p>
In unserer Gemeinde ist der Ambo (vom Pfarrer aus gesehen) rechts neben dem Altar platziert.
</p>

View File

@@ -0,0 +1,4 @@
<p>Die <a href="https://de.wikipedia.org/wiki/Kollekte" title="Kollekte auf Wikipedia">Kollekte</a> ist eine
Geldsammlung während oder am Ende eines Gottesdienstes.</p>
<p>Sie dient dazu, Geld für kirchliche, soziale oder wohltätige Zwecke zu sammeln.</p>
<p>Der Begriff kommt aus dem Lateinischen und bedeutet "sammeln".</p>

View File

@@ -0,0 +1,7 @@
<p>Die Farben der Gewänder folgen dem Kirchenjahr:</p>
<ul>
<li><strong>Grün</strong> Zeit im Jahreskreis</li>
<li><strong>Lila</strong> Fastenzeit und Advent</li>
<li><strong>Rot</strong> Feste des Heiligen Geistes und der Märtyrer</li>
<li><strong>Weiß/Gold</strong> Hochfeste wie Weihnachten und Ostern</li>
</ul>

View File

@@ -0,0 +1,13 @@
<p>Der Pfarrer wendet sich zur Gemeinde:</p>
<div class="dialog-box">
<p><span class="dialog-speaker">Pfarrer:</span> Der Herr sei mit euch.<br>
<span class="dialog-speaker">Gemeinde:</span> Und mit deinem Geist.
</p>
<p><span class="dialog-speaker">Pfarrer:</span> Erhebet die Herzen.<br>
<span class="dialog-speaker">Gemeinde:</span> Wir haben sie beim Herrn.
</p>
<p><span class="dialog-speaker">Pfarrer:</span> Lasset uns danken dem Herrn, unserm Gott.<br>
<span class="dialog-speaker">Gemeinde:</span> Das ist würdig und recht.
</p>
</div>
<p>„In Wahrheit ist es würdig und recht, dir, Herr, heiliger Vater, immer und überall zu danken…“</p>

View File

@@ -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"
}
]

30
mini/data/placements.json Normal file
View File

@@ -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"
}
]
}

View File

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

View File

@@ -0,0 +1,6 @@
[
{
"title": "Vater unser",
"file": "Vater_unser.html"
}
]

View File

@@ -0,0 +1,9 @@
Bespricht kurz, wer welche Aufgabe übernimmt.<br><br>
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.<br>
Der Kollekte-Dienst ist nicht immer vorgesehen, fragt daher sicherheitshalber beim Pfarrer nach, falls ihr unsicher
seid.
<br><br>
Stellt euch nun vor dem Kreuz in der Sakristei auf (so wie ihr später auch beim Einzug gehen werdet).<br>
Der Pfarrer sagt: "Unsere Hilfe ist im Namen des Herrn."<br>
Ihr antwortet: "Der Himmel und Erde erschaffen hat." und macht dabei das Kreuzzeichen.

View File

@@ -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.<br>
Stoßt die Glocke so an, dass sie etwa zweimal läutet.<br><br>
Danach geht ihr zu euren Knieplätzen auf der Stufe vor dem Altar.<br>
Sobald der Pfarrer ein Zeichen gibt (Handzeichen o. Ä.), macht ihr gemeinsam eine kurze Kniebeuge,<br>
geht dann hoch auf eure Sitzplätze und setzt euch möglichst synchron hin.

View File

@@ -0,0 +1 @@
EMPTY

View File

@@ -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"
}
]

72
mini/data/tasks.json Normal file
View File

@@ -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 23 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."
]
}
}

100
mini/index.html Normal file
View File

@@ -0,0 +1,100 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
<title>Ministranten Guide</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<header class="top-bar">
<div class="brand">
<div class="header-icon">
<img src="assets/icons/church.svg" alt="Ministranten Guide Icon">
</div>
<div>
<div class="header-title">Ministranten-Guide</div>
<div class="header-subtitle"></div>
</div>
</div>
<nav class="desktop-nav">
<a href="#" class="tab-btn active" data-file="pages/today.html">Heute</a>
<a href="#" class="tab-btn" data-file="pages/sequence.html">Ablauf</a>
<a href="#" class="tab-btn" data-file="pages/knowledge.html">Wissen</a>
</nav>
<button class="settings-btn" id="settingsBtn" aria-label="Einstellungen">
<img src="assets/icons/settings.svg" alt="Einstellungen">
</button>
</header>
<main class="content" id="content-container">
</main>
<nav class="bottom-nav">
<a href="#" class="bottom-tab active" data-file="pages/today.html">
<img src="assets/icons/sun.svg" alt="Heute Icon">
<span>Heute</span>
</a>
<a href="#" class="bottom-tab" data-file="pages/sequence.html">
<img src="assets/icons/list.svg" alt="Ablauf Icon">
<span>Ablauf</span>
</a>
<a href="#" class="bottom-tab" data-file="pages/knowledge.html">
<img src="assets/icons/book.svg" alt="Wissen Icon">
<span>Wissen</span>
</a>
</nav>
<div class="modal-overlay" id="settingsModal">
<div class="modal-content settings-modal">
<div class="modal-top-row">
<div class="modal-icon">
<img src="assets/icons/settings.svg" alt="Einstellungen Icon">
</div>
<button class="modal-close" id="closeSettingsBtn" aria-label="Schließen">
<img src="assets/icons/x.svg" alt="Schließen">
</button>
</div>
<h2 class="modal-title">Einstellungen</h2>
<div class="setting-item">
<div class="setting-label">
<span>Dark Mode</span>
</div>
<label class="switch">
<input type="checkbox" id="darkModeToggle">
<span class="slider round"></span>
</label>
</div>
<hr class="settings-divider">
<div class="settings-info">
<p><strong>Ministranten-Guide</strong> v0.1.0-beta</p>
<p>Entwickelt von <strong></strong></p>
<p>
<a href="https://github.com/dein-repo/ministranten-guide" target="_blank" rel="noopener">
GitHub Repository
</a>
</p>
<p>
<a href="https://github.com/dein-repo/ministranten-guide/issues/new" target="_blank" rel="noopener">
Problem melden (Issue)
</a>
</p>
</div>
</div>
</div>
<script type="module" src="js/main.js"></script>
</body>
</html>

102
mini/js/main.js Normal file
View File

@@ -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 = "<p>Error loading content.</p>";
console.error(error);
}
}
loadPage("pages/today.html");
triggers.forEach(trigger => {
trigger.addEventListener("click", (e) => {
e.preventDefault();
loadPage(trigger.getAttribute("data-file"));
});
});
});

View File

@@ -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 = `
<button class="wissen-header">
<span class="wissen-title">${title}</span>
<img src="assets/icons/arrow_down.svg" alt="Pfeil nach unten" class="wissen-arrow">
</button>
<div class="wissen-content">
<p class="loading">Loading...</p>
</div>
`;
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 = '<p>Failed to load content.</p>';
}
}
});
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);
});
}

120
mini/js/modules/sequence.js Normal file
View File

@@ -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 = '<p>Sequence could not be loaded.</p>';
}
}
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 = `
<button class="sequence-header">
<span class="sequence-title">${step.title}</span>
<i data-lucide="chevron-down" class="ablauf-arrow"></i>
</button>
<div class="sequence-content">
<p class="loading">Loading...</p>
</div>
`;
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 = '<p>Content could not be loaded.</p>';
}
}
});
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();
}

227
mini/js/modules/today.js Normal file
View File

@@ -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 => `<li>${step}</li>`).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;
}

13
mini/pages/knowledge.html Normal file
View File

@@ -0,0 +1,13 @@
<section class="wissen-page">
<div class="wissen-tabs">
<button class="wissen-tab active" data-tab="gebete">Gebete</button>
<button class="wissen-tab" data-tab="wissen">Wissen & Traditionen</button>
</div>
<div class="wissen-panel active" id="panel-gebete">
<div class="wissen-list" id="gebete-list"></div>
</div>
<div class="wissen-panel" id="panel-wissen">
<div class="wissen-list" id="wissen-list"></div>
</div>
</section>

20
mini/pages/sequence.html Normal file
View File

@@ -0,0 +1,20 @@
<section class="ablauf-page">
<div class="filter-bar">
<span class="filter-label">Aufgaben:</span>
<div class="filter-chips">
<button class="filter-chip active" data-filter="alle">Alle</button>
<button class="filter-chip" data-filter="allgemein">Allgemein</button>
<button class="filter-chip" data-filter="gabenbereitung">Gabenbereitung</button>
<button class="filter-chip" data-filter="kerze">Kerze</button>
<button class="filter-chip" data-filter="koerbchen">Kollekte</button>
<button class="filter-chip" data-filter="weihrauch">Weihrauch</button>
</div>
</div>
<div class="sequence-list" id="sequence-list"></div>
<div class="ablauf-download-bar">
<a href="pdf/ablauf-uebersicht.pdf" download class="btn-download btn-download-large">
<img src="assets/icons/download.svg" alt="Download Icon"> Gesamten Ablauf als PDF herunterladen
</a>
</div>
</section>

161
mini/pages/today.html Normal file
View File

@@ -0,0 +1,161 @@
<section class="today-dashboard">
<article class="card today-summary-card">
<div class="summary-date">
<span id="weekday">Sonntag</span>
<span id="date">15. August 2026</span>
</div>
<div class="summary-details">
<div class="summary-item">
<img src="assets/icons/shirt.svg" alt="Shirt Icon">
<div>
<strong>Gewand</strong>
<span id="summary-color">Grün</span>
</div>
</div>
<div class="summary-item">
<img src="assets/icons/users.svg" alt="Users Icon">
<div>
<strong>Ministranten</strong>
<span id="summary-count">4</span>
</div>
</div>
</div>
</article>
<article class="card liturgical-card">
<div class="liturgical-title-row">
<h3>Gewand</h3>
</div>
<p class="card-hint">
Die Gewandfarbe richtet sich nach dem Kirchenjahr. Ihr übernehmt einfach die Farbe, die auch der Pfarrer
trägt.<br>
<small>(Bei Weiß oder Gold zieht ihr stattdessen Grün an.)</small>
</p>
<div class="info-stack">
<div class="info-line">
<div class="info-line-icon info-line-icon-green">
<img src="assets/icons/shirt.svg" alt="Shirt Icon">
</div>
<div>
<strong>Grünes Gewand</strong>
<span>An Tagen im Jahreskreis</span>
</div>
</div>
<div class="info-line">
<div class="info-line-icon info-line-icon-lilac">
<img src="assets/icons/shirt.svg" alt="Shirt Icon">
</div>
<div>
<strong>Lilanes Gewand</strong>
<span>An Tagen der Buße und Fastenzeit</span>
</div>
</div>
<div class="info-line">
<div class="info-line-icon info-line-icon-red">
<img src="assets/icons/shirt.svg" alt="Shirt Icon">
</div>
<div>
<strong>Rotes Gewand</strong>
<span>An Festtagen des Heiligen Geistes und der Märtyrer</span>
</div>
</div>
</div>
</article>
<article class="card placement-card">
<div class="placement-header">
<h3>Sitzordnung</h3>
<button class="placement-toggle" id="incense-toggle">
<img src="assets/images/tasks/incense.svg" alt="Weihrauch Icon" class="task-icon"> Weihrauch
</button>
</div>
<div class="placement-stage">
<img id="placement-background" src="assets/images/placement/background.svg" alt="Platzierung Altar">
<img id="placement-image" src="assets/images/placement/entrance.svg" alt="Platzierung Einzug">
</div>
<div class="placement-controls">
<button id="placement-prev"><img src="assets/icons/arrow_left.svg" alt="Pfeil Links Icon"></button>
<span id="placement-label">Einzug</span>
<button id="placement-next"><img src="assets/icons/arrow_right.svg" alt="Pfeil Rechts Icon"></button>
</div>
<div class="placement-legend">
<div class="legend-item"><img src="assets/images/tasks/bread-wine.svg" alt="Gabenbereitung Icon"
class="task-icon"> Gabenbereitung</div>
<div class="legend-item"><img src="assets/images/tasks/candle.svg" alt="Kerze Icon" class="task-icon">
<p id="kerze-label">Kerze</p>
</div>
<div class="legend-item" id="basket-toggle"><img src="assets/images/tasks/basket.svg" alt="Kollekte Icon"
class="task-icon">
Kollekte</div>
<div class="legend-item" id="incense-legend"><img src="assets/images/tasks/incense.svg" alt="Weihrauch Icon"
class="task-icon"> Weihrauch</div>
</div>
</article>
<div class="card pdf-download-card">
<div class="pdf-download-content">
<img src="assets/icons/file.svg" alt="File Icon">
<div>
<strong>Kurzübersicht</strong>
<span>Gewand, Platzierungen & Aufgaben als PDF</span>
</div>
</div>
<a href="pdf/heute-kurzuebersicht.pdf" download class="btn-download">
<img src="assets/icons/download.svg" alt="Download Icon"> PDF herunterladen
</a>
</div>
<div class="tasks-section">
<h2>Deine Aufgaben</h2>
<div class="tasks-grid">
<button class="task-card" data-task="gabenbereitung">
<image src="assets/images/tasks/bread-wine.svg" alt="Gabenbereitung Icon">
<span class="task-name">Gabenbereitung</span>
<span class="task-arrow"><img src="assets/icons/arrow_right.svg" alt="Pfeil Icon"></span>
</button>
<button class="task-card" data-task="kerze">
<img src="assets/images/tasks/candle.svg" alt="Kerze Icon">
<span class="task-name">Kerze</span>
<span class="task-arrow"><img src="assets/icons/arrow_right.svg" alt="Pfeil Icon"></span>
</button>
<button class="task-card" data-task="kollekte">
<img src="assets/images/tasks/basket.svg" alt="Kollekte Icon">
<span class="task-name">Kollekte</span>
<span class="task-arrow"><img src="assets/icons/arrow_right.svg" alt="Pfeil Icon"></span>
</button>
<button class="task-card" data-task="weihrauch">
<img src="assets/images/tasks/incense.svg" alt="Weihrauch Icon">
<span class="task-name">Weihrauch</span>
<span class="task-arrow"><img src="assets/icons/arrow_right.svg" alt="Pfeil Icon"></span>
</button>
</div>
</div>
<div class="modal-overlay" id="task-modal">
<div class="modal-content">
<div class="modal-header">
<div class="modal-top-row">
<div class="modal-icon">
<img id="modal-icon" src="" alt="Aufgaben Icon">
</div>
<button class="modal-close" aria-label="Schließen">
<img src="assets/icons/x.svg" alt="Schließen Icon">
</button>
</div>
<h3 class="modal-title" id="modal-title">Titel</h3>
<p class="modal-description" id="modal-description">
Hier steht eine kurze Erklärung.
</p>
</div>
<div id="task-segmented-control" class="segmented-control" style="display:none;"></div>
<ol class="modal-steps" id="modal-steps">
<li>Schritt 1</li>
<li>Schritt 2</li>
</ol>
</div>
</div>
</section>

1538
mini/style.css Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +0,0 @@
<!DOCTYPE html><html lang="de"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Snake</title><style>body{margin:0;display:flex;justify-content:center;align-items:center;height:100vh;background:#000;}canvas{background:#222}</style>
</head><body><canvas id="c"></canvas><script>let c=document.getElementById('c'),x=c.getContext('2d'),p=20,t=[{x:200,y:200}],f={x:60,y:60},d=39;c.width=c.height=400;
setInterval(()=>{t.unshift({x:(t[0].x+(d==37?-p:d==39?p:0)+c.width)%c.width,y:(t[0].y+(d==38?-p:d==40?p:0)+c.height)%c.height});
if(t[0].x==f.x&&t[0].y==f.y)f={x:~~(Math.random()*20)*p,y:~~(Math.random()*20)*p};else t.pop();x.fillStyle='lime';x.clearRect(0,0,c.width,c.height);t.forEach(e=>x.fillRect(e.x,e.y,p,p));
x.fillStyle='red';x.fillRect(f.x,f.y,p,p);if(t.slice(1).some(e=>e.x==t[0].x&&e.y==t[0].y))t=[{x:200,y:200}],f={x:60,y:60},d=39;},100);onkeydown=e=>d=e.keyCode-32&&e.keyCode;
</script></body></html>

175
projects.html Normal file
View File

@@ -0,0 +1,175 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>BR0TCRAFT | Projekte</title>
<link rel="stylesheet" href="style.css">
<link
href="https://fonts.googleapis.com/css2?family=Fira+Code:wght@400;600&family=Inter:wght@300;400;600;800&family=Orbitron:wght@700&display=swap"
rel="stylesheet">
<style>
.project-status {
display: inline-block;
font-family: var(--font-code);
font-size: 0.68rem;
padding: 2px 7px;
border-radius: 3px;
margin-bottom: 0.5rem;
}
.status-active {
background: rgba(77, 255, 128, 0.08);
color: #4dff80;
border: 1px solid rgba(77, 255, 128, 0.15);
}
.status-wip {
background: rgba(255, 180, 0, 0.08);
color: #ffb400;
border: 1px solid rgba(255, 180, 0, 0.15);
}
.status-done {
background: rgba(255, 255, 255, 0.04);
color: #555;
border: 1px solid #222;
}
.card-stack {
font-size: 0.78rem;
font-family: var(--font-code);
color: var(--accent-primary);
margin-top: auto;
padding-top: 0.75rem;
}
.project-links {
display: flex;
gap: 0.6rem;
margin-top: 0.75rem;
flex-wrap: wrap;
}
.link-btn {
font-size: 0.78rem;
font-family: var(--font-code);
color: var(--text-muted);
background: rgba(255, 255, 255, 0.04);
border: 1px solid #222;
border-radius: 4px;
padding: 3px 10px;
text-decoration: none;
transition: 0.2s;
}
.link-btn:hover {
color: #fff;
border-color: #444;
}
.link-btn.primary {
color: var(--accent-primary);
border-color: rgba(255, 77, 77, 0.25);
}
.link-btn.primary:hover {
background: rgba(255, 77, 77, 0.08);
}
</style>
</head>
<body>
<nav class="navbar">
<div class="brand">BR0TCRAFT</div>
<div class="nav-links">
<a href="index.html">Home</a>
<a href="projects.html" class="active">Projects</a>
<a href="tutorials.html">Dev Notes</a>
<a href="about.html">About</a>
<a href="assets.html">Assets</a>
</div>
</nav>
<div class="container">
<section class="section">
<h1 style="font-family: var(--font-display); margin-bottom: 0.5rem; color: #fff;">Projects</h1>
<p style="color: var(--text-muted); font-size: 0.9rem; margin-bottom: 3rem;">
Dinge, die ich gebaut habe oder noch baue.
</p>
<div class="grid">
<article class="card">
<a href="extensions.html" class="card-image-link">
<div
style="height: 150px; background: linear-gradient(135deg, #1a0000, #ff4d4d22); display: flex; align-items: center; justify-content: center;">
<h2
style="font-family: var(--font-display); color: var(--accent-primary); letter-spacing: 1px; font-size: 1.5rem;">
EXTENSIONS</h2>
</div>
</a>
<div class="card-content">
<span class="project-status status-active">aktiv</span>
<h3 class="card-title"><a href="extensions.html">Scratch Extensions</a></h3>
<p class="card-desc">Meine Sammlung an Erweiterungen für TurboWarp, PenguinMod und SE!.</p>
<div class="card-stack">JS · TurboWarp API</div>
<div class="project-links">
<a href="extensions.html" class="link-btn primary">→ öffnen</a>
</div>
</div>
</article>
<article class="card">
<a href="/br0tkabeln/" class="card-image-link">
<div style="height: 150px; background: #1a0000; overflow: hidden;">
<img src="img/Apps/Br0tkabeln.jpg" style="width:100%; height:100%; object-fit: cover;" alt="Br0tkabeln">
</div>
</a>
<div class="card-content">
<span class="project-status status-active">aktiv</span>
<h3 class="card-title"><a href="/br0tkabeln/">Br0tkabeln</a></h3>
<p class="card-desc">Simpler Vokabeltrainer mit Fokus auf Privacy. Keine Accounts, keine Cloud.</p>
<div class="card-stack">Python · Flask · JS</div>
<div class="project-links">
<a href="/br0tkabeln/" class="link-btn primary">→ öffnen</a>
<a href="https://github.com/Br0tcraft" target="_blank" class="link-btn">GitHub</a>
</div>
</div>
</article>
<article class="card">
<div style="height: 150px; background: #1a0000; overflow: hidden;">
<img src="img/Apps/snake-large.png" style="width:100%; height:100%; object-fit: cover;" alt="Snake">
</div>
<div class="card-content">
<span class="project-status status-done">abgeschlossen</span>
<h3 class="card-title">Tiny Snake</h3>
<p class="card-desc">Ein Rewrite des Klassikers. Hauptsächlich um C++ besser zu lernen.</p>
<div class="card-stack">C++</div>
<div class="project-links">
<a href="https://github.com/Br0tcraft" target="_blank" class="link-btn">GitHub</a>
</div>
</div>
</article>
<article class="card">
<div style="height: 150px; background: #111; display: flex; align-items: center; justify-content: center;">
<span style="font-family: var(--font-code); font-size: 0.75rem; color: #2a2a2a;">in Arbeit</span>
</div>
<div class="card-content">
<span class="project-status status-wip">⧖ in Arbeit</span>
<h3 class="card-title">Demnächst</h3>
<p class="card-desc">Weitere Projekte folgen. Kein ETA.</p>
</div>
</article>
</div>
</section>
</div>
</body>
</html>

1204
style.css

File diff suppressed because it is too large Load Diff

438
tutorials.html Normal file
View File

@@ -0,0 +1,438 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>BR0TCRAFT | Dev Notes</title>
<link rel="stylesheet" href="style.css">
<link
href="https://fonts.googleapis.com/css2?family=Fira+Code:wght@400;600&family=Inter:wght@300;400;600;800&family=Orbitron:wght@700&display=swap"
rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/atom-one-dark.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
<style>
.search-wrapper {
margin-bottom: 2.5rem;
}
.search-bar {
width: 100%;
padding: 1rem 1.5rem;
border-radius: var(--radius);
background: rgba(255, 255, 255, 0.04);
border: 1px solid #2a2a2a;
color: #fff;
font-family: var(--font-body);
font-size: 1rem;
transition: 0.2s;
}
.search-bar:focus {
outline: none;
border-color: var(--accent-primary);
box-shadow: 0 0 15px var(--accent-glow);
}
.search-bar::placeholder {
color: #444;
}
/* Tabs: Notes / Tutorials */
.tab-switcher {
display: flex;
gap: 0.25rem;
margin-bottom: 2.5rem;
border-bottom: 1px solid #222;
padding-bottom: 0;
}
.tab-btn {
background: none;
border: none;
color: #555;
font-family: var(--font-code);
font-size: 0.85rem;
padding: 0.6rem 1.2rem;
cursor: pointer;
border-bottom: 2px solid transparent;
margin-bottom: -1px;
transition: color 0.2s, border-color 0.2s;
}
.tab-btn.active {
color: #fff;
border-bottom-color: var(--accent-primary);
}
.tab-btn:hover:not(.active) {
color: #aaa;
}
.tab-panel {
display: none;
}
.tab-panel.active {
display: block;
}
/* Dev Note card */
.note-card {
border: 1px solid #1f1f1f;
border-radius: var(--radius);
padding: 1.5rem;
margin-bottom: 1rem;
background: rgba(255, 255, 255, 0.015);
transition: border-color 0.2s;
}
.note-card:hover {
border-color: #333;
}
.note-header {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 1rem;
margin-bottom: 0.8rem;
flex-wrap: wrap;
}
.note-index {
font-family: var(--font-code);
font-size: 0.72rem;
color: #444;
margin-bottom: 0.2rem;
}
.note-card h3 {
color: #fff;
font-size: 1rem;
margin: 0;
}
.note-tag {
font-family: var(--font-code);
font-size: 0.72rem;
color: var(--accent-primary);
background: rgba(255, 77, 77, 0.07);
padding: 2px 8px;
border-radius: 4px;
white-space: nowrap;
}
.note-problem,
.note-solution {
font-size: 0.88rem;
line-height: 1.6;
margin-bottom: 0.5rem;
}
.note-problem {
color: #666;
}
.note-problem strong,
.note-solution strong {
font-family: var(--font-code);
font-size: 0.78rem;
letter-spacing: 0.04em;
}
.note-problem strong {
color: #555;
}
.note-solution strong {
color: var(--accent-primary);
}
.note-solution {
color: var(--text-muted);
}
.note-card pre {
margin: 0.75rem 0 0;
border-radius: 6px;
overflow-x: auto;
font-size: 0.82rem;
}
.note-card pre code {
font-family: var(--font-code);
}
/* Tutorial cards (same as before but slimmer) */
.meta-tags {
display: flex;
gap: 0.5rem;
margin-top: 0.75rem;
flex-wrap: wrap;
}
.meta-tag {
font-size: 0.78rem;
padding: 2px 8px;
border-radius: 4px;
background: #1e1e1e;
color: #555;
font-family: var(--font-code);
}
.notes-count {
font-family: var(--font-code);
font-size: 0.78rem;
color: #444;
margin-bottom: 1.5rem;
}
</style>
</head>
<body>
<nav class="navbar">
<div class="brand">BR0TCRAFT</div>
<div class="nav-links">
<a href="index.html">Home</a>
<a href="projects.html">Projects</a>
<a href="tutorials.html" class="active">Dev Notes</a>
<a href="about.html">About</a>
<a href="assets.html">Assets</a>
</div>
</nav>
<div class="container">
<section class="section">
<h1 style="font-family: var(--font-display); margin-bottom: 0.5rem; color: #fff;">Dev Notes</h1>
<p style="color: var(--text-muted); margin-bottom: 2rem; font-size: 0.95rem;">
Notizen, Quick Tips und längere Artikel.
</p>
<div class="search-wrapper">
<input type="text" id="searchInput" class="search-bar" placeholder="Suche (C++, Python, Git ...)">
</div>
<div class="tab-switcher">
<button class="tab-btn active" onclick="switchTab('notes', this)">Quick Tips</button>
<button class="tab-btn" onclick="switchTab('tutorials', this)">Tutorials</button>
</div>
<div class="tab-panel active" id="tab-notes">
<div class="notes-count" id="notesCount"></div>
<div class="note-card searchable" data-keywords="c++ string string_view performance copy">
<div class="note-header">
<div class="note-title-group">
<div class="note-index">#01</div>
<h3>std::string vs. std::string_view</h3>
</div>
<span class="note-tag">C++</span>
</div>
<p class="note-problem"><strong>PROBLEM</strong> &nbsp;Unnötige Kopien beim Übergeben von Strings als
Funktionsparameter.</p>
<p class="note-solution"><strong>LÖSUNG</strong> &nbsp;Wenn der String nur gelesen wird,
<code>std::string_view</code> statt <code>const std::string&amp;</code> nutzen. Kein Ownership, kein
Copy-Overhead.
</p>
<pre><code class="language-cpp">void log(std::string_view msg) {
std::cout &lt;&lt; msg;
}</code></pre>
</div>
<div class="note-card searchable" data-keywords="python dict keyerror get default">
<div class="note-header">
<div class="note-title-group">
<div class="note-index">#02</div>
<h3>dict.get() statt dict[]</h3>
</div>
<span class="note-tag">Python</span>
</div>
<p class="note-problem"><strong>PROBLEM</strong> &nbsp;<code>KeyError</code> beim Zugriff auf einen Key, der
nicht existieren könnte.</p>
<p class="note-solution"><strong>LÖSUNG</strong> &nbsp;<code>.get()</code> gibt <code>None</code> (oder einen
Default) zurück, statt zu crashen.</p>
<pre><code class="language-python">val = data.get("key", "default")</code></pre>
</div>
<div class="note-card searchable" data-keywords="git commit message amend fix">
<div class="note-header">
<div class="note-title-group">
<div class="note-index">#03</div>
<h3>Falschen Commit-Message fixen</h3>
</div>
<span class="note-tag">Git</span>
</div>
<p class="note-problem"><strong>PROBLEM</strong> &nbsp;Commit abgeschickt, Message falsch.</p>
<p class="note-solution"><strong>LÖSUNG</strong> &nbsp;<code>--amend</code> vor dem Push. Danach wird's
komplizierter.</p>
<pre><code class="language-bash">git commit --amend -m "Richtige Message"</code></pre>
</div>
<div class="note-card searchable" data-keywords="c++ destructor virtual base class memory leak">
<div class="note-header">
<div class="note-title-group">
<div class="note-index">#04</div>
<h3>Vergessenes virtual im Destruktor</h3>
</div>
<span class="note-tag">C++</span>
</div>
<p class="note-problem"><strong>PROBLEM</strong> &nbsp;Basisklassen-Destruktor ist nicht <code>virtual</code>
→ Derived-Objekte werden nicht korrekt zerstört.</p>
<p class="note-solution"><strong>LÖSUNG</strong> &nbsp;Sobald eine Klasse als Basisklasse gedacht ist:</p>
<pre><code class="language-cpp">class Base {
public:
virtual ~Base() = default;
};</code></pre>
</div>
<div class="note-card searchable" data-keywords="javascript optional chaining nullish coalescing">
<div class="note-header">
<div class="note-title-group">
<div class="note-index">#05</div>
<h3>Optional Chaining statt verschachteltem if</h3>
</div>
<span class="note-tag">JavaScript</span>
</div>
<p class="note-problem"><strong>PROBLEM</strong> &nbsp;Tief verschachtelte Objekte ohne Garantie, dass jede
Ebene existiert.</p>
<p class="note-solution"><strong>LÖSUNG</strong> &nbsp;<code>?.</code> und <code>??</code> kombinieren.
Kürzer, kein <code>TypeError</code>, kein 3-stöckiges <code>if</code>.</p>
<pre><code class="language-javascript">const city = user?.address?.city ?? "Unbekannt";</code></pre>
</div>
</div>
<div class="tab-panel" id="tab-tutorials">
<div class="grid" id="tutorialGrid">
<article class="card tutorial-card searchable" data-keywords="3ds devkitpro homebrew cia 3dsx rsf">
<div class="card-content">
<span
style="color:var(--accent-primary); font-family:var(--font-code); font-size:0.78rem;">#Nintendo3DS</span>
<h3 class="card-title" style="margin-top:0.5rem;">Introduction to 3DS Homebrew Development</h3>
<p class="card-desc">How to start programming homebrew for the Nintendo 3DS.</p>
<div class="meta-tags">
<span class="meta-tag">homebrew</span>
<span class="meta-tag">3DS</span>
<span class="meta-tag">C++</span>
</div>
<a href="article.html?doc=3ds/" class="btn" style="margin-top:auto; font-size:0.85rem;">Read
More</a>
</div>
</article>
<article class="card tutorial-card searchable" data-keywords="server account verwaltung passwort sicherheit">
<div class="card-content">
<span
style="color:var(--accent-primary); font-family:var(--font-code); font-size:0.78rem;">#Backend</span>
<h3 class="card-title" style="margin-top:0.5rem;">Account-Verwaltung</h3>
<p class="card-desc">Accounts handhaben, ohne Angst zu haben, Passwörter zu leaken.</p>
<div class="meta-tags">
<span class="meta-tag">Python</span>
<span class="meta-tag">Security</span>
<span class="meta-tag">Webserver</span>
</div>
<a href="article.html?file=content/account-management.md" class="btn"
style="margin-top:auto; font-size:0.85rem;">Lesen</a>
</div>
</article>
<article class="card tutorial-card searchable"
data-keywords="email verifikation authentifizierung backend login">
<div class="card-content">
<span
style="color:var(--accent-primary); font-family:var(--font-code); font-size:0.78rem;">#Backend</span>
<h3 class="card-title" style="margin-top:0.5rem;">Email-Authentifizierung</h3>
<p class="card-desc">Emails im Backend verschicken, Benutzer über Login-Code verifizieren.</p>
<div class="meta-tags">
<span class="meta-tag">Python</span>
<span class="meta-tag">Backends</span>
<span class="meta-tag">Webserver</span>
</div>
<a href="article.html?file=content/email-auth.md" class="btn"
style="margin-top:auto; font-size:0.85rem;">Lesen</a>
</div>
</article>
<article class="card tutorial-card searchable" data-keywords="passwort salting hashing datenbank sicherheit">
<div class="card-content">
<span
style="color:var(--accent-primary); font-family:var(--font-code); font-size:0.78rem;">#Backend</span>
<h3 class="card-title" style="margin-top:0.5rem;">Passwörter hashen &amp; salzen</h3>
<p class="card-desc">Auswirkung eines Datenbankleaks minimieren, so geht's richtig.</p>
<div class="meta-tags">
<span class="meta-tag">Python</span>
<span class="meta-tag">Security</span>
<span class="meta-tag">Backends</span>
</div>
<a href="article.html?file=content/password-salting.md" class="btn"
style="margin-top:auto; font-size:0.85rem;">Lesen</a>
</div>
</article>
<article class="card tutorial-card searchable" data-keywords="datenbanken speichern benutzer accounts sql">
<div class="card-content">
<span
style="color:var(--accent-primary); font-family:var(--font-code); font-size:0.78rem;">#Backend</span>
<h3 class="card-title" style="margin-top:0.5rem;">Datenbanken</h3>
<p class="card-desc">Daten, Benutzer und Accounts in einer Datenbank speichern.</p>
<div class="meta-tags">
<span class="meta-tag">Python</span>
<span class="meta-tag">Backends</span>
<span class="meta-tag">Webserver</span>
</div>
<a href="article.html?file=content/databases.md" class="btn"
style="margin-top:auto; font-size:0.85rem;">Lesen</a>
</div>
</article>
</div>
</div>
</section>
</div>
<script>
function switchTab(name, btn) {
document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active'));
document.querySelectorAll('.tab-panel').forEach(p => p.classList.remove('active'));
btn.classList.add('active');
document.getElementById('tab-' + name).classList.add('active');
filterSearch(document.getElementById('searchInput').value);
}
const input = document.getElementById('searchInput');
function filterSearch(term) {
const t = term.toLowerCase();
const activePanel = document.querySelector('.tab-panel.active');
const items = activePanel.querySelectorAll('.searchable');
let visible = 0;
items.forEach(item => {
const text = item.innerText.toLowerCase();
const kw = (item.getAttribute('data-keywords') || '').toLowerCase();
const show = !t || text.includes(t) || kw.includes(t);
item.style.display = show ? '' : 'none';
if (show) visible++;
});
const countEl = document.getElementById('notesCount');
if (countEl && document.getElementById('tab-notes').classList.contains('active')) {
countEl.textContent = visible + ' Einträge';
}
}
input.addEventListener('input', e => filterSearch(e.target.value));
window.addEventListener('DOMContentLoaded', () => {
hljs.highlightAll();
const notes = document.querySelectorAll('#tab-notes .searchable');
document.getElementById('notesCount').textContent = notes.length + ' Einträge';
});
</script>
</body>
</html>