1
0
Files
test.br0tcraft.de/extensions.html
2026-08-20 20:36:47 +02:00

388 lines
12 KiB
HTML

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