Files
rustguac/static/docs.html
T
Dave Kempe 67101e27ce Initial public release
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-06 14:38:53 +11:00

224 lines
9.1 KiB
HTML

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>rustguac - Docs</title>
<style>
:root { --primary: #e94560; --primary-hover: #c73652; --accent: #5bc0be; --accent-hover: #4aa3a1; --bg: #1a1a2e; --surface: #16213e; --input: #0f3460; --text: #e0e0e0; --text-muted: #aaa; --border: #333; }
* { box-sizing: border-box; }
body { font-family: monospace; background: var(--bg); color: var(--text); padding: 2em; font-size: 18px; margin: 0; }
h1 { color: var(--primary); }
nav { margin-bottom: 1.5em; font-size: 0.95em; }
nav a { margin-right: 1.5em; text-decoration: none; color: var(--accent); }
nav a:hover { text-decoration: underline; }
nav .active { color: var(--primary); font-weight: bold; }
nav .logout { color: #888; float: right; cursor: pointer; }
nav .logout:hover { color: var(--primary); }
/* Layout */
.docs-layout { display: flex; min-height: calc(100vh - 140px); border-top: 1px solid var(--border); margin-top: 1em; }
/* Sidebar */
.sidebar {
width: 220px;
min-width: 220px;
border-right: 1px solid var(--border);
padding: 1em 0;
overflow-y: auto;
}
.sidebar a {
display: block;
padding: 0.5em 1.2em;
color: var(--text-muted);
text-decoration: none;
font-size: 0.9em;
border-left: 3px solid transparent;
}
.sidebar a:hover { color: var(--text); background: var(--surface); }
.sidebar a.active {
color: var(--primary);
border-left-color: var(--primary);
background: var(--surface);
font-weight: bold;
}
/* Content area */
.content {
flex: 1;
overflow-y: auto;
padding: 2em 2.5em;
max-width: 900px;
}
.content h1 { color: var(--primary); font-size: 1.5em; margin-bottom: 0.8em; }
.content h2 { color: var(--primary); font-size: 1.15em; margin-top: 2em; margin-bottom: 0.5em; border-bottom: 1px solid var(--border); padding-bottom: 0.3em; }
.content h3 { color: var(--accent); font-size: 1em; margin-top: 1.5em; margin-bottom: 0.4em; }
.content h4 { color: var(--accent); font-size: 0.95em; margin-top: 1.2em; margin-bottom: 0.3em; }
.content p { margin: 0.6em 0; line-height: 1.6; }
.content ul, .content ol { margin: 0.5em 0 0.5em 1.5em; line-height: 1.6; }
.content li { margin: 0.2em 0; }
.content a { color: var(--accent); }
.content a:hover { text-decoration: underline; }
.content code {
background: var(--surface);
padding: 0.15em 0.4em;
border-radius: 3px;
font-size: 0.9em;
}
.content pre {
background: var(--surface);
border: 1px solid var(--border);
padding: 1em;
border-radius: 6px;
overflow-x: auto;
line-height: 1.5;
margin: 0.8em 0;
}
.content pre code {
background: none;
padding: 0;
border-radius: 0;
font-size: 0.85em;
}
.content table { border-collapse: collapse; width: 100%; margin: 0.8em 0; }
.content th, .content td { text-align: left; padding: 0.4em 0.8em; border-bottom: 1px solid var(--border); }
.content th { color: var(--text-muted); font-size: 0.85em; }
.content blockquote {
border-left: 3px solid var(--accent);
margin: 0.8em 0;
padding: 0.4em 1em;
color: var(--text-muted);
}
.content hr { border: none; border-top: 1px solid var(--border); margin: 2em 0; }
.loading { padding: 2em; color: var(--text-muted); }
@media (max-width: 700px) {
.sidebar { width: 160px; min-width: 160px; }
.content { padding: 1em; }
}
</style>
</head>
<body>
<img id="site-logo" src="/logo.svg" style="max-height:40px;vertical-align:middle;margin-right:0.5em" alt=""><h1 style="display:inline;vertical-align:middle">rustguac</h1>
<nav>
<a href="/addressbook.html">Address Book</a>
<a href="/sessions.html" id="sessions-link">Sessions</a>
<a href="/recordings.html">Recordings</a>
<a href="/docs.html" class="active">Docs</a>
<a href="/admin.html" id="admin-link" style="display:none">Admin</a>
<span class="logout" id="logout-btn">logout</span>
</nav>
<div class="docs-layout">
<div class="sidebar" id="sidebar"></div>
<div class="content" id="content">
<div class="loading">Loading documentation...</div>
</div>
</div>
<script>
var docs = [];
var sidebar = document.getElementById('sidebar');
var content = document.getElementById('content');
function showSection(slug) {
var doc = docs.find(function(d) { return d.slug === slug; });
if (!doc && docs.length) doc = docs[0];
if (!doc) return;
content.innerHTML = doc.html;
// Update sidebar active state
var links = sidebar.querySelectorAll('a');
for (var i = 0; i < links.length; i++) {
links[i].classList.toggle('active', links[i].dataset.slug === doc.slug);
}
// Update hash without triggering hashchange
if (location.hash !== '#' + doc.slug) {
history.replaceState(null, '', '#' + doc.slug);
}
content.scrollTop = 0;
}
function getSlugFromHash() {
return location.hash ? location.hash.substring(1) : '';
}
// Load docs from API
fetch('/api/docs')
.then(function(r) { return r.json(); })
.then(function(data) {
docs = data;
// Build sidebar
var html = '';
for (var i = 0; i < docs.length; i++) {
html += '<a href="#' + docs[i].slug + '" data-slug="' + docs[i].slug + '">' + docs[i].title + '</a>';
}
sidebar.innerHTML = html;
// Click handler for sidebar links
sidebar.addEventListener('click', function(e) {
var a = e.target.closest('a');
if (!a) return;
e.preventDefault();
var slug = a.dataset.slug;
history.pushState(null, '', '#' + slug);
showSection(slug);
});
// Show initial section from hash or first
showSection(getSlugFromHash() || docs[0].slug);
})
.catch(function() {
content.innerHTML = '<div class="loading">Failed to load documentation.</div>';
});
// Handle browser back/forward
window.addEventListener('popstate', function() {
showSection(getSlugFromHash());
});
// Theme and auth
function applyTheme(theme) {
if (!theme) return;
var props = {primary_color:'--primary',primary_hover:'--primary-hover',accent_color:'--accent',accent_hover:'--accent-hover',bg_color:'--bg',surface_color:'--surface',input_color:'--input',text_color:'--text',text_muted:'--text-muted',border_color:'--border'};
var r = document.documentElement.style;
for (var k in props) { if (theme[k]) r.setProperty(props[k], theme[k]); }
if (theme.logo_url) { var logo = document.getElementById('site-logo'); if (logo) { logo.src = theme.logo_url; logo.style.display = ''; } }
}
fetch('/api/auth/status').then(function(r){return r.json()}).then(function(d){
if(d.site_title){document.title=d.site_title+' - Docs';document.querySelector('h1').textContent=d.site_title;}
applyTheme(d.theme);
});
var apiKey = sessionStorage.getItem('rustguac_api_key');
var roleLevel = { admin: 4, poweruser: 3, operator: 2, viewer: 1 };
if (apiKey) {
document.getElementById('admin-link').style.display = '';
} else {
fetch('/api/me', { credentials: 'same-origin' })
.then(function(res) {
if (!res.ok) { window.location.href = '/'; return; }
return res.json();
})
.then(function(data) {
if (!data) return;
if (data.role === 'admin') document.getElementById('admin-link').style.display = '';
var level = roleLevel[data.role] || 0;
if (level < 3) {
document.getElementById('sessions-link').style.display = 'none';
}
})
.catch(function() { window.location.href = '/'; });
}
document.getElementById('logout-btn').addEventListener('click', function() {
sessionStorage.removeItem('rustguac_api_key');
fetch('/auth/logout', { credentials: 'same-origin' })
.finally(function() { window.location.href = '/'; });
});
</script>
</body>
</html>