librelad 16eda07b3d fix(webui): make SSH Access page full-width like config/admin pages
The SSH Access page was boxed to max-width 860px and centered, unlike the
Overview and System admin pages (.admin-page) which span the full content
width. Drop the cap and match .admin-page padding so /admin/tools/ssh-access
looks like the rest of the Admin area.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-25 22:12:50 +01:00

67 lines
3.0 KiB
JavaScript

// LibrePortal site — interactions (mobile drawer, copy, scrollspy, reveal, app filter)
(() => {
// mobile drawer (same behaviour as the dashboard)
const menuToggle = document.getElementById('mobile-menu-toggle');
const drawer = document.getElementById('mobile-drawer');
if (menuToggle && drawer) {
menuToggle.addEventListener('click', () => {
const open = drawer.classList.toggle('mobile-open');
document.body.style.overflow = open ? 'hidden' : '';
});
drawer.querySelectorAll('a').forEach((a) =>
a.addEventListener('click', () => { drawer.classList.remove('mobile-open'); document.body.style.overflow = ''; }));
}
// copy the install command
const cmdEl = document.querySelector('[data-install-cmd]');
const CMD = cmdEl ? cmdEl.getAttribute('data-install-cmd') : '';
document.querySelectorAll('.copy').forEach((btn) => {
btn.addEventListener('click', async () => {
try { await navigator.clipboard.writeText(CMD); }
catch { const t = document.createElement('textarea'); t.value = CMD; document.body.appendChild(t); t.select(); document.execCommand('copy'); t.remove(); }
btn.classList.add('done');
const span = btn.querySelector('span'); const prev = span.textContent; span.textContent = 'Copied ✓';
setTimeout(() => { btn.classList.remove('done'); span.textContent = prev; }, 1700);
});
});
// scrollspy → light up the active topbar pill
const spies = [...document.querySelectorAll('.nav-item[data-spy]')];
if (spies.length) {
const spyIO = new IntersectionObserver((entries) => {
entries.forEach((e) => {
if (e.isIntersecting) spies.forEach((s) => s.classList.toggle('nav-active', s.dataset.spy === e.target.id));
});
}, { rootMargin: '-45% 0px -50% 0px', threshold: 0 });
spies.map((s) => document.getElementById(s.dataset.spy)).filter(Boolean).forEach((s) => spyIO.observe(s));
}
// reveal on scroll
const io = new IntersectionObserver((entries) => {
entries.forEach((e) => { if (e.isIntersecting) { e.target.classList.add('in'); io.unobserve(e.target); } });
}, { threshold: 0.16, rootMargin: '0px 0px -8% 0px' });
document.querySelectorAll('.reveal, .stagger').forEach((el) => io.observe(el));
// app category filter
const filters = document.querySelector('.app-filters');
if (filters) {
const cards = [...document.querySelectorAll('.app-card')];
const countEl = document.querySelector('.app-count');
const update = (cat) => {
let shown = 0;
cards.forEach((c) => {
const match = cat === 'all' || (c.dataset.cats || '').split(' ').includes(cat);
c.classList.toggle('hidden', !match);
if (match) shown++;
});
if (countEl) countEl.textContent = `${shown} app${shown === 1 ? '' : 's'}`;
};
filters.addEventListener('click', (e) => {
const chip = e.target.closest('.chip');
if (!chip) return;
filters.querySelectorAll('.chip').forEach((c) => c.classList.toggle('active', c === chip));
update(chip.dataset.cat);
});
}
})();