<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>rfheadless docs</title>
<link rel="stylesheet" href="assets/site.css">
</head>
<body>
<header class="site-header">
<div class="wrapper header-row">
<button id="sidebar-toggle" class="sidebar-toggle" aria-label="Toggle sidebar">☰</button>
<div class="site-title"><a href="#/">rfheadless</a></div>
</div>
</header>
<div class="docs-layout">
<aside class="sidebar" id="sidebar" aria-label="Documentation navigation">
<div class="sidebar-header">
<div class="logo"><img src="assets/images/logo.png" alt="rfheadless logo"></div>
<div class="brand">rfheadless</div>
</div>
<div class="sidebar-search">
<input id="nav-search" placeholder="Filter pages…" aria-label="Filter pages" />
</div>
<nav id="nav" class="nav-list" role="navigation"></nav>
</aside>
<main class="content" id="content">
<h1>rfheadless</h1>
<p>Welcome to the rfheadless documentation. Use the navigation to browse pages.</p>
</main>
</div>
<footer class="site-footer wrapper">
<small>© rfheadless — docs generated from repository Markdown</small>
</footer>
<script src="assets/vendor/marked.min.js"></script>
<script src="assets/vendor/dompurify.min.js"></script>
<script>
function makeLink(title, path) {
const a = document.createElement('a');
a.href = '#/' + path;
a.className = 'nav-link';
a.textContent = title;
a.addEventListener('click', (e) => { e.preventDefault(); navigate(path); });
a.dataset.path = path;
return a;
}
function makeSection(section) {
const div = document.createElement('div');
div.className = 'nav-section';
const header = document.createElement('div');
header.className = 'nav-section-header';
const title = document.createElement('span');
title.textContent = section.title;
header.appendChild(title);
const toggle = document.createElement('button');
toggle.className = 'nav-toggle';
toggle.setAttribute('aria-expanded','true');
toggle.innerHTML = '▾';
header.appendChild(toggle);
div.appendChild(header);
const ul = document.createElement('ul');
ul.className = 'nav-sub';
(section.children || []).forEach(child => {
const li = document.createElement('li');
li.appendChild(makeLink(child.title, child.path));
ul.appendChild(li);
});
div.appendChild(ul);
toggle.addEventListener('click', () => {
const expanded = toggle.getAttribute('aria-expanded') === 'true';
toggle.setAttribute('aria-expanded', String(!expanded));
ul.style.display = expanded ? 'none' : 'block';
toggle.innerHTML = expanded ? '▸' : '▾';
});
return div;
}
async function loadIndex() {
const res = await fetch('./docs.json');
const pages = await res.json();
const nav = document.getElementById('nav');
pages.forEach(p => {
if (p.children) {
nav.appendChild(makeSection(p));
} else {
const div = document.createElement('div');
div.className = 'nav-item';
div.appendChild(makeLink(p.title, p.path));
nav.appendChild(div);
}
});
const search = document.getElementById('nav-search');
search.addEventListener('input', (e) => {
const q = e.target.value.trim().toLowerCase();
filterNav(q);
});
window.addEventListener('keydown', (ev) => {
if (ev.key === '/' && document.activeElement.tagName !== 'INPUT') {
ev.preventDefault();
document.getElementById('nav-search').focus();
}
});
}
function filterNav(q) {
document.querySelectorAll('.nav-item, .nav-section').forEach(el => {
if (el.classList.contains('nav-section')) {
const sub = Array.from(el.querySelectorAll('.nav-link'));
const matched = sub.some(a => a.textContent.toLowerCase().includes(q));
el.style.display = matched ? '' : 'none';
} else {
const a = el.querySelector('.nav-link');
if (a) el.style.display = a.textContent.toLowerCase().includes(q) ? '' : 'none';
}
}); }
function setActive(path) {
document.querySelectorAll('.nav-link').forEach(n => {
if (n.dataset.path === path) n.classList.add('active'); else n.classList.remove('active');
});
}
async function navigate(path, push=true) {
const content = document.getElementById('content');
content.innerHTML = '<p>Loading…</p>';
try {
const mdRes = await fetch(path);
if (!mdRes.ok) { content.innerHTML = '<p>Page not found</p>'; return; }
const md = await mdRes.text();
const rawHtml = marked.parse(md);
const safeHtml = DOMPurify.sanitize(rawHtml);
content.innerHTML = safeHtml;
setActive(path);
if (push) history.pushState({path}, '', '#/' + path);
window.scrollTo(0,0);
if (window.innerWidth < 900) document.getElementById('sidebar').classList.remove('open');
} catch (e) {
content.innerHTML = '<p>Failed to load page</p>';
console.error(e);
}
}
window.addEventListener('popstate', (ev) => {
const state = ev.state;
if (state && state.path) navigate(state.path, false);
else {
document.getElementById('content').innerHTML = '<h1>rfheadless</h1><p>Welcome to the rfheadless documentation. Use the navigation to browse pages.</p>';
}
});
document.getElementById('sidebar-toggle').addEventListener('click', () => {
document.getElementById('sidebar').classList.toggle('open');
});
(async function(){ await loadIndex();
const hash = location.hash.replace('#/','');
if (hash) navigate(hash, false);
})();
</script>
</body>
</html>