Wiki Setup Guide
Welcome to your new Wiki Node. To enable content, please ensure your repository structure matches the config.
-Required Structure
-/ (Root)
-├── index.html
-├── .nojekyll <-- IMPORTANT for GitHub Pages
-├── assets/
-│ └── bjorn.png
-└── wiki/
- ├── structure.json
- └── 01_General/
- └── Introduction.md
- Page Not Found
${cleanError} -
- Troubleshooting:
- 1. Ensure the file exists in your wiki/ folder.
- 2. Check capitalization (Linux/GitHub is case-sensitive).
- 3. Verify .nojekyll is at the root of your repo.
-
Unable to load changelog (GitHub API Rate Limit might be reached). Please check back later.
`; + document.getElementById('versions-list').innerHTML = `Unable to load changelog. Check back later.
`; } } @@ -1375,14 +1331,54 @@ document.body.style.overflow = ''; } - // Mobile Menu + // Mobile Menu & TOC Logic const sidebar = document.getElementById('sidebar'); + const tocSidebar = document.getElementById('mobile-toc-sidebar'); const overlay = document.getElementById('overlay'); - function openMenu() { sidebar.classList.remove('-translate-x-full'); overlay.classList.remove('hidden'); setTimeout(() => overlay.classList.remove('opacity-0'), 10); } - function closeMenu() { sidebar.classList.add('-translate-x-full'); overlay.classList.add('opacity-0'); setTimeout(() => overlay.classList.add('hidden'), 300); } + + function openMenu() { + closeTOC(); // Close TOC if open + sidebar.classList.remove('-translate-x-full'); + overlay.classList.remove('hidden'); + setTimeout(() => overlay.classList.remove('opacity-0'), 10); + } + function closeMenu() { + sidebar.classList.add('-translate-x-full'); + checkOverlay(); + } + + function openTOC() { + // Only open if TOC has items + const list = document.getElementById('mobile-toc-list'); + if (!list.hasChildNodes() || list.innerHTML.includes('No sections')) return; + + closeMenu(); // Close Menu if open + tocSidebar.classList.remove('translate-x-full'); + overlay.classList.remove('hidden'); + setTimeout(() => overlay.classList.remove('opacity-0'), 10); + } + function closeTOC() { + tocSidebar.classList.add('translate-x-full'); + checkOverlay(); + } + + function checkOverlay() { + // Hide overlay only if both panels are closed + if (sidebar.classList.contains('-translate-x-full') && tocSidebar.classList.contains('translate-x-full')) { + overlay.classList.add('opacity-0'); + setTimeout(() => overlay.classList.add('hidden'), 300); + } + } + document.getElementById('menu-btn').onclick = openMenu; document.getElementById('close-sidebar-btn').onclick = closeMenu; - overlay.onclick = closeMenu; + document.getElementById('close-toc-btn').onclick = closeTOC; + + // Unified overlay click closes whichever is open + overlay.onclick = () => { + closeMenu(); + closeTOC(); + }; // Events document.addEventListener('keydown', (e) => { @@ -1390,7 +1386,10 @@ if (e.key === 'Escape') { if (document.activeElement === searchInput) searchInput.blur(); else if (lightbox.classList.contains('active')) closeLightbox(); - else closeMenu(); + else { + closeMenu(); + closeTOC(); + } } }); @@ -1407,7 +1406,6 @@ const scrollPercent = (scrollHeight > 0) ? (scrollTop / scrollHeight) * 100 : 0; progressBar.style.width = scrollPercent + '%'; - // Show/Hide Scroll Top Button if (scrollTop > 300) scrollBtn.classList.remove('hidden'); else scrollBtn.classList.add('hidden'); }); @@ -1416,16 +1414,17 @@ scrollBtn.onclick = () => scrollContainer.scrollTo({ top: 0, behavior: 'smooth' }); } } + // --- MOBILE SWIPE GESTURES --- - // Detect swipe from left edge → open menu let touchStartX = 0; let touchStartY = 0; let touchEndX = 0; let touchEndY = 0; - const MIN_SWIPE_DISTANCE = 50; // minimal movement to count as a swipe - const MAX_VERTICAL_DRIFT = 80; // tolerance to avoid accidental scroll/swipe mix - const EDGE_ZONE = 200; // swipe must start within 200px from screen left + const MIN_SWIPE_DISTANCE = 50; + const MAX_VERTICAL_DRIFT = 80; + const EDGE_ZONE_LEFT = 200; // Swipe from left triggers Menu + const EDGE_ZONE_RIGHT = 100; // Distance from right edge to likely trigger TOC document.addEventListener("touchstart", (e) => { const t = e.changedTouches[0]; @@ -1442,22 +1441,31 @@ document.addEventListener("touchend", () => { const dx = touchEndX - touchStartX; const dy = Math.abs(touchEndY - touchStartY); + const screenW = window.innerWidth; - // 1. SWIPE → RIGHT = OPEN MENU - if ( - touchStartX < EDGE_ZONE && // gesture started at screen left - dx > MIN_SWIPE_DISTANCE && // swiped enough to the right - dy < MAX_VERTICAL_DRIFT // not a vertical scroll - ) { - openMenu(); + // 1. SWIPE LEFT -> RIGHT (Open Menu or Close TOC) + if (dx > MIN_SWIPE_DISTANCE && dy < MAX_VERTICAL_DRIFT) { + // If TOC is open, close it + if (!tocSidebar.classList.contains('translate-x-full')) { + closeTOC(); + } + // Else if swipe starts near left edge, open Menu + else if (touchStartX < EDGE_ZONE_LEFT) { + openMenu(); + } } - // 2. SWIPE → LEFT = CLOSE MENU - if ( - dx < -MIN_SWIPE_DISTANCE && // swipe left - dy < MAX_VERTICAL_DRIFT // again avoid scroll false positives - ) { - closeMenu(); + // 2. SWIPE RIGHT -> LEFT (Open TOC or Close Menu) + if (dx < -MIN_SWIPE_DISTANCE && dy < MAX_VERTICAL_DRIFT) { + // If Menu is open, close it + if (!sidebar.classList.contains('-translate-x-full')) { + closeMenu(); + } + // Else if swipe starts anywhere (or strictly right side if preferred), open TOC + // User asked for "swipe right to left" generally, but preventing conflict with horizontal scroll tables is good + else { + openTOC(); + } } });