added IMAP, LetsEncrypt, update layout

This commit is contained in:
2026-08-12 21:14:19 +01:00
parent 6e103959b0
commit 70fa1a5f2c
222 changed files with 42947 additions and 14038 deletions
+64 -1
View File
@@ -5,6 +5,14 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{block "title" .}}Email Server Management{{end}}</title>
<script>
// Applied before first paint so an unpinned sidebar starts collapsed, not
// pinned-then-flashing-collapsed once the stylesheet/JS below catches up.
if (localStorage.getItem('sidebarPinned') === 'false') {
document.documentElement.classList.add('sidebar-unpinned');
}
</script>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.css" rel="stylesheet">
@@ -13,6 +21,17 @@
body { background-color: #1a1a1a; color: #e0e0e0; }
.main-container { display: flex; min-height: 100vh; }
.content-area { flex: 1; margin-left: var(--sidebar-width); padding: 20px; transition: margin-left 0.3s ease; }
/* Unpinned sidebar: collapsed off-screen, floats over full-width content until
pinned again. Revealed by the toggle button or by moving the mouse to the
left edge (see JS below); hidden again on mouseleave. */
html.sidebar-unpinned .sidebar { transform: translateX(-100%); }
html.sidebar-unpinned .sidebar.sidebar-open { transform: translateX(0); box-shadow: 4px 0 24px rgba(0, 0, 0, 0.5); }
html.sidebar-unpinned .content-area { margin-left: 0 !important; }
/* Sits in the page-title bar, not fixed over the page — so when the sidebar
peeks open (z-index above content), it naturally covers this button instead
of the button floating on top of the sidebar's own header. */
.sidebar-toggle-btn { display: none; }
html.sidebar-unpinned .sidebar-toggle-btn { display: inline-flex; }
.navbar-brand { color: #fff !important; }
.card { background-color: #2d2d2d; border: 1px solid #404040; }
.table-dark { --bs-table-bg: #2d2d2d; --bs-table-border-color: #404040; }
@@ -52,7 +71,10 @@
<div class="content-area">
<nav class="navbar navbar-expand-lg navbar-dark bg-dark mb-4">
<div class="container-fluid">
<span class="navbar-brand mb-0 h1">
<span class="navbar-brand mb-0 h1 d-flex align-items-center">
<button id="sidebarToggleBtn" class="btn btn-sm btn-outline-light me-2 sidebar-toggle-btn" title="Show sidebar" onclick="showSidebarPeek()">
<i class="bi bi-list"></i>
</button>
<i class="bi bi-envelope-fill me-2"></i>
{{block "page_title" .}}Email Server Management{{end}}
</span>
@@ -118,6 +140,47 @@
setInterval(updateTime, 1000);
updateTime();
// Sidebar pin/unpin — pinned (default) keeps today's always-visible behavior.
// Unpinned collapses it off-screen so the content area gets the full width;
// it re-appears as an overlay via the toggle button or by nudging the mouse to
// the left edge, and hides again once the mouse leaves it.
function isSidebarPinned() { return localStorage.getItem('sidebarPinned') !== 'false'; }
function updateSidebarPinUI() {
const icon = document.getElementById('sidebarPinIcon');
const btn = document.getElementById('sidebarPinBtn');
if (!icon || !btn) return;
const pinned = isSidebarPinned();
icon.className = pinned ? 'bi bi-pin-angle-fill' : 'bi bi-pin-angle';
btn.title = pinned ? 'Unpin sidebar (auto-hide)' : 'Pin sidebar (keep open)';
}
function hideSidebarPeek() {
const sidebarEl = document.querySelector('.sidebar');
if (sidebarEl) sidebarEl.classList.remove('sidebar-open');
}
function showSidebarPeek() {
const sidebarEl = document.querySelector('.sidebar');
if (sidebarEl) sidebarEl.classList.add('sidebar-open');
}
function toggleSidebarPin() {
const pinned = !isSidebarPinned();
localStorage.setItem('sidebarPinned', pinned ? 'true' : 'false');
document.documentElement.classList.toggle('sidebar-unpinned', !pinned);
updateSidebarPinUI();
hideSidebarPeek();
}
document.addEventListener('DOMContentLoaded', function() {
updateSidebarPinUI();
const sidebarEl = document.querySelector('.sidebar');
if (sidebarEl) {
sidebarEl.addEventListener('mouseleave', function() {
if (!isSidebarPinned()) hideSidebarPeek();
});
}
document.addEventListener('mousemove', function(e) {
if (!isSidebarPinned() && e.clientX <= 15) showSidebarPeek();
});
});
// Notifications auto-dismiss after 5s, but hovering (reading, or selecting
// text to copy) pauses the timer — it only resumes once the mouse leaves.
// Clicking inside never dismisses; only the X button or the timer does.