438 lines
15 KiB
HTML
438 lines
15 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 | 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> Unnötige Kopien beim Übergeben von Strings als
|
|
Funktionsparameter.</p>
|
|
<p class="note-solution"><strong>LÖSUNG</strong> Wenn der String nur gelesen wird,
|
|
<code>std::string_view</code> statt <code>const std::string&</code> nutzen. Kein Ownership, kein
|
|
Copy-Overhead.
|
|
</p>
|
|
<pre><code class="language-cpp">void log(std::string_view msg) {
|
|
std::cout << 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> <code>KeyError</code> beim Zugriff auf einen Key, der
|
|
nicht existieren könnte.</p>
|
|
<p class="note-solution"><strong>LÖSUNG</strong> <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> Commit abgeschickt, Message falsch.</p>
|
|
<p class="note-solution"><strong>LÖSUNG</strong> <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> Basisklassen-Destruktor ist nicht <code>virtual</code>
|
|
→ Derived-Objekte werden nicht korrekt zerstört.</p>
|
|
<p class="note-solution"><strong>LÖSUNG</strong> 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> Tief verschachtelte Objekte ohne Garantie, dass jede
|
|
Ebene existiert.</p>
|
|
<p class="note-solution"><strong>LÖSUNG</strong> <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 & 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> |