mirror of
https://github.com/sol1/rustguac.git
synced 2026-10-26 13:39:54 +00:00
be866264bd
reports: show entry names as host over a tenant subline Session history and the most-used table showed "host (Tenant)" names wrapped across three lines; they now read as the host over a quieter tenant line, as on Connections and Recordings. Part of #254 sessions, recordings: show the session type with the shared badge (#254) The sessions page printed the type as plain text and the recordings page as a coloured word; connections and reports already used .type-badge from rustguac.css. All four now use the same badge. The Proxmox badge colour moves into the shared stylesheet with the others. Shared facet filters (static/filters.js) on the sessions, recordings, reports and connections pages: a dropdown per facet (Type, Folder, User, Owner, Status as each page has them), multi-select with counts that narrow as other facets apply, All/None, a find box for long lists, a Clear filters link, and selections kept per page in sessionStorage. They sit beside the existing text search and combine with it; the buttons are compacted to the height and padding of the other controls and the dropdown reserves room for its scrollbar so it no longer sits over the counts. The list pages share one toolbar: a borderless row with the search box, the facets and the page action on the right (New ad-hoc session, Export CSV), placeholders standardised to 'Search <noun> (fields…)'. Sessions gains a search (user, host, owner, ID, type) and a User facet; Reports moves from its own .filter-bar to the shared one. The redundant page titles go — the nav already names and marks the active page — and section headings centre their heading and action button on one line. Connections: the folder header is sticky at the top of the entries pane, so it stays under the nav and the active-sessions strip while a long table scrolls. All four pages show the session type with the shared .type-badge (the Proxmox colour moves into rustguac.css), reports and recordings show "host (Tenant)" names as the host over a quieter tenant line, and the key glyph before Credentials in the nav is sized to the text. Part of #254 tests: shared facet filters and the list toolbar on every list page
230 lines
12 KiB
HTML
230 lines
12 KiB
HTML
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<title>rustguac - Docs</title>
|
|
<link rel="stylesheet" href="/rustguac.css">
|
|
<style>
|
|
.docs-layout {
|
|
display: flex;
|
|
min-height: calc(100vh - 180px);
|
|
border-top: 1px solid var(--border);
|
|
margin-top: var(--s-3);
|
|
}
|
|
.sidebar {
|
|
width: 240px; min-width: 240px;
|
|
border-right: 1px solid var(--border);
|
|
padding: var(--s-3) 0;
|
|
overflow-y: auto;
|
|
}
|
|
.sidebar a {
|
|
display: block;
|
|
padding: var(--s-2) var(--s-4);
|
|
color: var(--text-muted);
|
|
text-decoration: none;
|
|
font-size: var(--fz-sm);
|
|
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 {
|
|
flex: 1;
|
|
overflow-y: auto;
|
|
padding: var(--s-4) var(--s-5);
|
|
max-width: 960px;
|
|
}
|
|
.content h1 { color: var(--primary); font-size: var(--fz-xl); margin-bottom: var(--s-3); }
|
|
.content h2 {
|
|
color: var(--primary); font-size: var(--fz-lg);
|
|
margin-top: var(--s-5); margin-bottom: var(--s-2);
|
|
border-bottom: 1px solid var(--border); padding-bottom: var(--s-2);
|
|
}
|
|
.content h3 { color: var(--accent); font-size: var(--fz-md); margin-top: var(--s-4); margin-bottom: var(--s-1); }
|
|
.content h4 { color: var(--accent); font-size: var(--fz-sm); margin-top: var(--s-3); margin-bottom: var(--s-1); }
|
|
.content p { margin: var(--s-2) 0; line-height: 1.65; }
|
|
.content ul, .content ol { margin: var(--s-2) 0 var(--s-2) var(--s-4); line-height: 1.65; }
|
|
.content li { margin: var(--s-1) 0; }
|
|
.content a { color: var(--accent); }
|
|
.content a:hover { text-decoration: underline; }
|
|
.content code {
|
|
background: var(--surface);
|
|
padding: 2px var(--s-1);
|
|
border-radius: var(--radius);
|
|
font-size: 0.9em;
|
|
}
|
|
.content pre {
|
|
background: var(--surface);
|
|
border: 1px solid var(--border);
|
|
padding: var(--s-3);
|
|
border-radius: var(--radius);
|
|
overflow-x: auto;
|
|
line-height: 1.5;
|
|
margin: var(--s-2) 0;
|
|
}
|
|
.content pre code { background: none; padding: 0; border-radius: 0; font-size: var(--fz-sm); }
|
|
.content table { border-collapse: collapse; width: 100%; margin: var(--s-2) 0; }
|
|
.content th, .content td {
|
|
text-align: left; padding: var(--s-2) var(--s-3);
|
|
border-bottom: 1px solid var(--border); font-size: var(--fz-sm);
|
|
}
|
|
.content th { color: var(--text-muted); text-transform: none; letter-spacing: 0; background: transparent; }
|
|
.content blockquote {
|
|
border-left: 3px solid var(--accent);
|
|
margin: var(--s-2) 0;
|
|
padding: var(--s-1) var(--s-3);
|
|
color: var(--text-muted);
|
|
}
|
|
.content hr { border: none; border-top: 1px solid var(--border); margin: var(--s-5) 0; }
|
|
.loading { padding: var(--s-4); color: var(--text-muted); }
|
|
@media (max-width: 700px) {
|
|
.sidebar { width: 180px; min-width: 180px; }
|
|
.content { padding: var(--s-3); }
|
|
}
|
|
</style>
|
|
<script>(function(){var c=localStorage.getItem('rustguac_theme_colors');if(c){try{var o=JSON.parse(c),r=document.documentElement.style;for(var k in o)r.setProperty('--'+k.replace(/_/g,'-'),o[k]);if(o.bg_pattern&&o.bg_pattern!=='none'){var s=document.createElement('style');s.id='bg-pattern-style';s.textContent='body{background-image:'+o.bg_pattern+';background-attachment:fixed}';document.head.appendChild(s)}}catch(e){}}})();</script>
|
|
</head>
|
|
<body>
|
|
<div class="brand-row">
|
|
<img id="site-logo" src="/logo.svg" alt="">
|
|
<h1>rustguac</h1>
|
|
</div>
|
|
<nav>
|
|
<a href="/connections.html">Connections</a>
|
|
<a href="/sessions.html" id="sessions-link" data-min-role="poweruser">Sessions</a>
|
|
<a href="/recordings.html">Recordings</a>
|
|
<a href="/reports.html" id="reports-link" data-min-role="poweruser">Reports</a>
|
|
<a href="/docs.html" class="active">Docs</a>
|
|
<a href="/tokens.html" id="tokens-link" data-min-role="operator">Tokens</a>
|
|
<a href="/admin.html" id="admin-link" data-min-role="admin">Admin</a>
|
|
<a href="/connections.html#my-credentials" id="my-creds-nav" title="Manage your saved credential variables"><span class="nav-key">🔑</span> Credentials</a>
|
|
<span id="user-menu-wrapper"><a href="#" id="logout-item">Logout</a><span id="user-menu-btn" title="Preferences">⚙ Settings</span><div id="user-menu"><div class="um-section-label">Theme</div><div id="um-theme-list"></div></div></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 applyThemeColors(colors){var r=document.documentElement.style;for(var k in colors)r.setProperty('--'+k.replace(/_/g,'-'),colors[k]);var s=document.getElementById('bg-pattern-style');if(!s){s=document.createElement('style');s.id='bg-pattern-style';document.head.appendChild(s)}s.textContent=colors.bg_pattern&&colors.bg_pattern!=='none'?'body{background-image:'+colors.bg_pattern+';background-attachment:fixed}':'';localStorage.setItem('rustguac_theme_colors',JSON.stringify(colors))}
|
|
var _themePresets={},_adminPreset='aurora';
|
|
var _themeDescriptions={dark:'Navy & cyan \u2014 the default',light:'Clean white & blue','high-contrast':'Maximum readability',terminal:'Retro green-on-black',nord:'Arctic, muted blues',corporate:'Slate & steel blue',aurora:'Midnight blue with ambient glow',jaguar:'Racing green & gold'};
|
|
function initTheme(t){if(!t)return;_themePresets=t.presets||{};_adminPreset=t.admin_preset||'aurora';var u=localStorage.getItem('rustguac_theme'),active=u&&_themePresets[u]?u:_adminPreset,colors=(active===_adminPreset)?t.admin_colors:_themePresets[active];if(colors)applyThemeColors(colors);if(t.logo_url){var l=document.getElementById('site-logo');if(l){if(l.src!==t.logo_url&&!l.src.endsWith(t.logo_url))l.src=t.logo_url;l.style.display=''}}var menu=document.getElementById('um-theme-list');if(menu){menu.innerHTML='';Object.keys(_themePresets).forEach(function(name){var item=document.createElement('div');item.className='um-item'+(name===active?' active':'');var sw=document.createElement('span');sw.className='um-swatch';var p=_themePresets[name];sw.style.background='linear-gradient(135deg,'+p.primary+' 50%,'+p.accent+' 50%)';item.appendChild(sw);var info=document.createElement('div');info.className='um-theme-info';var nm=document.createElement('span');nm.className='um-theme-name';nm.textContent=name;info.appendChild(nm);var desc=document.createElement('span');desc.className='um-theme-desc';desc.textContent=_themeDescriptions[name]||'';info.appendChild(desc);item.appendChild(info);item.addEventListener('click',function(){localStorage.setItem('rustguac_theme',name);applyThemeColors(_themePresets[name]);menu.querySelectorAll('.um-item').forEach(function(el){el.classList.remove('active')});item.classList.add('active');document.getElementById('user-menu').style.display='none'});menu.appendChild(item)})}}
|
|
var _ub=document.getElementById('user-menu-btn');if(_ub)_ub.addEventListener('click',function(e){e.stopPropagation();var m=document.getElementById('user-menu');m.style.display=m.style.display==='block'?'none':'block'});document.addEventListener('click',function(){var m=document.getElementById('user-menu');if(m)m.style.display='none'});
|
|
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;}
|
|
initTheme(d.theme);
|
|
});
|
|
var apiKey = sessionStorage.getItem('rustguac_api_key');
|
|
var roleLevel = { admin: 4, poweruser: 3, operator: 2, viewer: 1 };
|
|
if (apiKey) {
|
|
// A key is never on the page GET, so the server served the anonymous variant.
|
|
document.documentElement.setAttribute('data-role-level', '4');
|
|
document.getElementById('admin-link').style.display = '';
|
|
document.getElementById('tokens-link').style.display = '';
|
|
document.getElementById('reports-link').style.display = '';
|
|
} else {
|
|
fetch('/api/me', { credentials: 'same-origin' })
|
|
.then(function(res) {
|
|
if (res.status === 401 || res.status === 403) { window.location.href = '/'; return; }
|
|
if (!res.ok) 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;
|
|
// Reconcile the server-written nav gate (see rustguac.css): the page
|
|
// GET carried no API key, so key users arrive on the anonymous variant.
|
|
document.documentElement.setAttribute('data-role-level', String((typeof apiKey !== 'undefined' && apiKey) ? 4 : level));
|
|
if (level >= 2) document.getElementById('tokens-link').style.display = '';
|
|
if (level >= 3) {
|
|
document.getElementById('reports-link').style.display = '';
|
|
}
|
|
if (level < 3) {
|
|
document.getElementById('sessions-link').style.display = 'none';
|
|
}
|
|
})
|
|
.catch(function() {});
|
|
}
|
|
document.getElementById('logout-item').addEventListener('click', function() {
|
|
sessionStorage.removeItem('rustguac_api_key');
|
|
fetch('/auth/logout', { credentials: 'same-origin' })
|
|
.finally(function() { window.location.href = '/'; });
|
|
});
|
|
</script>
|
|
</body>
|
|
</html>
|