mirror of
https://github.com/ghostersk/gowebmail.git
synced 2026-09-13 23:30:37 +01:00
786 lines
55 KiB
HTML
786 lines
55 KiB
HTML
{{template "base" .}}
|
||
{{define "title"}}GoWebMail{{end}}
|
||
{{define "body_class"}}app-page{{end}}
|
||
|
||
{{define "body"}}
|
||
<div class="app" id="app-root" data-mob-view="list">
|
||
<!-- Mobile top bar (hidden on desktop) -->
|
||
<div class="mob-topbar" id="mob-topbar">
|
||
<button class="mob-nav-btn" id="mob-nav-btn" onclick="mobShowNav()" title="Menu">
|
||
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z"/></svg>
|
||
</button>
|
||
<button class="mob-back-btn" id="mob-back-btn" onclick="mobBack()" title="Back" style="display:none">
|
||
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z"/></svg>
|
||
</button>
|
||
<span class="mob-title" id="mob-title">GoWebMail</span>
|
||
<button class="compose-btn" onclick="openCompose()" style="margin-left:auto;padding:5px 10px;font-size:11px">+ New</button>
|
||
<button class="compose-btn" onclick="window.open('/compose','_blank')" style="padding:5px 8px;font-size:11px" title="Compose in new tab">↗</button>
|
||
</div>
|
||
|
||
<!-- Sidebar -->
|
||
<aside class="sidebar">
|
||
<div class="sidebar-header">
|
||
<div style="display:flex;align-items:center;justify-content:space-between">
|
||
<div style="display:flex;align-items:center;gap:8px;min-width:0">
|
||
<button class="icon-btn sidebar-collapse-btn" onclick="toggleSidebarCollapse()" title="Collapse sidebar" style="flex-shrink:0">
|
||
<svg viewBox="0 0 24 24" width="15" height="15" fill="currentColor"><path d="M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z"/></svg>
|
||
</button>
|
||
<div class="logo">
|
||
<div class="logo-icon"><svg viewBox="0 0 24 24"><path d="M20 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 4l-8 5-8-5V6l8 5 8-5v2z"/></svg></div>
|
||
<span class="logo-text"><a href="/">GoWebMail</a></span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div style="display:flex;margin-top:8px">
|
||
<button class="compose-btn" onclick="openCompose()" style="flex:1;border-radius:6px 0 0 6px">+ New</button>
|
||
<button class="compose-btn" onclick="toggleComposeDropdown(event)" style="border-radius:0 6px 6px 0;border-left:1px solid rgba(255,255,255,.25);padding:6px 7px" title="More options">
|
||
<svg viewBox="0 0 24 24" width="10" height="10" fill="white"><path d="M7 10l5 5 5-5z"/></svg>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="nav-section">
|
||
<div class="nav-item active" id="nav-unified" onclick="selectFolder('unified','Unified Inbox')">
|
||
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M20 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 4l-8 5-8-5V6l8 5 8-5v2z"/></svg>
|
||
Unified Inbox
|
||
<span class="unread-badge" id="unread-total" style="display:none"></span>
|
||
</div>
|
||
<div class="nav-item" id="nav-starred" onclick="selectFolder('starred','Starred')">
|
||
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z"/></svg>
|
||
Starred
|
||
</div>
|
||
<div class="nav-item" id="nav-contacts" onclick="showContacts()">
|
||
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M20 0H4v2h16V0zM0 4v18h24V4H0zm22 16H2V6h20v14zM12 11c1.66 0 3-1.34 3-3s-1.34-3-3-3-3 1.34-3 3 1.34 3 3 3zm-6 6c0-2.21 2.69-4 6-4s6 1.79 6 4H6z"/></svg>
|
||
Contacts
|
||
</div>
|
||
<div class="nav-item" id="nav-calendar" onclick="showCalendar()">
|
||
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M20 3h-1V1h-2v2H7V1H5v2H4c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 18H4V8h16v13z"/></svg>
|
||
Calendar
|
||
</div>
|
||
<div id="folders-by-account"></div>
|
||
</div>
|
||
|
||
<div class="sidebar-footer">
|
||
<div class="user-info">
|
||
<span class="user-name" id="user-display">...</span>
|
||
<a href="/admin" id="admin-link" style="display:none;font-size:11px;color:var(--accent);text-decoration:none">Server Administration</a>
|
||
</div>
|
||
<div class="footer-actions">
|
||
<button class="icon-btn" onclick="openSettings()" title="Settings">
|
||
<svg viewBox="0 0 24 24"><path d="M19.14 12.94c.04-.3.06-.61.06-.94 0-.32-.02-.64-.07-.94l2.03-1.58c.18-.14.23-.41.12-.61l-1.92-3.32c-.12-.22-.37-.29-.59-.22l-2.39.96c-.5-.38-1.03-.7-1.62-.94l-.36-2.54c-.04-.24-.24-.41-.48-.41h-3.84c-.24 0-.43.17-.47.41l-.36 2.54c-.59.24-1.13.57-1.62.94l-2.39-.96c-.22-.08-.47 0-.59.22L2.74 8.87c-.12.21-.08.47.12.61l2.03 1.58c-.05.3-.09.63-.09.94s.02.64.07.94l-2.03 1.58c-.18.14-.23.41-.12.61l1.92 3.32c.12.22.37.29.59.22l2.39-.96c.5.38 1.03.7 1.62.94l.36 2.54c.05.24.24.41.48.41h3.84c.24 0 .44-.17.47-.41l.36-2.54c.59-.24 1.13-.56 1.62-.94l2.39.96c.22.08.47 0 .59-.22l1.92-3.32c.12-.22.07-.47-.12-.61l-2.01-1.58zM12 15.6c-1.98 0-3.6-1.62-3.6-3.6s1.62-3.6 3.6-3.6 3.6 1.62 3.6 3.6-1.62 3.6-3.6 3.6z"/></svg>
|
||
</button>
|
||
<button class="icon-btn" onclick="doLogout()" title="Sign out">
|
||
<svg viewBox="0 0 24 24"><path d="M17 7l-1.41 1.41L18.17 11H8v2h10.17l-2.58 2.58L17 17l5-5zM4 5h8V3H4c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h8v-2H4V5z"/></svg>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</aside>
|
||
<!-- Mobile sidebar backdrop -->
|
||
<div class="mob-sidebar-backdrop" id="mob-sidebar-backdrop" onclick="mobCloseNav()"></div>
|
||
|
||
<!-- Mail view: message list + reading pane (position/density configurable via View menu) -->
|
||
<div class="mail-view" id="mail-view">
|
||
<div class="message-list-panel">
|
||
<div class="panel-header">
|
||
<div style="display:flex;align-items:center;gap:6px;min-width:0">
|
||
<button class="icon-btn" id="sidebar-expand-btn" onclick="toggleSidebarCollapse()" title="Show sidebar" style="flex-shrink:0">
|
||
<svg viewBox="0 0 24 24" width="15" height="15" fill="currentColor"><path d="M8.59 16.59L13.17 12 8.59 7.41 10 6l6 6-6 6z"/></svg>
|
||
</button>
|
||
<span class="panel-title" id="panel-title">Unified Inbox</span>
|
||
</div>
|
||
<div style="display:flex;align-items:center;gap:6px">
|
||
<span class="panel-count" id="panel-count"></span>
|
||
<div class="filter-dropdown" id="view-dropdown">
|
||
<button class="filter-dropdown-btn" id="view-dropdown-btn" title="View settings" onclick="toggleViewDropdown(event)">
|
||
<svg width="13" height="13" viewBox="0 0 24 24" fill="currentColor"><path d="M12 4.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5zM12 17c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z"/></svg>
|
||
<span>View</span>
|
||
</button>
|
||
<div class="filter-dropdown-menu" id="view-dropdown-menu" style="display:none;min-width:190px">
|
||
<div style="padding:6px 12px 2px;font-size:10px;text-transform:uppercase;letter-spacing:.6px;color:var(--muted)">Reading pane</div>
|
||
<div class="filter-opt" id="vopt-pane-right" onclick="setViewPref('readingPane','right');event.stopPropagation()">✓ Right</div>
|
||
<div class="filter-opt" id="vopt-pane-bottom" onclick="setViewPref('readingPane','bottom');event.stopPropagation()">○ Bottom</div>
|
||
<div class="filter-sep-line"></div>
|
||
<div style="padding:6px 12px 2px;font-size:10px;text-transform:uppercase;letter-spacing:.6px;color:var(--muted)">Density</div>
|
||
<div class="filter-opt" id="vopt-density-compact" onclick="setViewPref('density','compact');event.stopPropagation()">✓ Compact</div>
|
||
<div class="filter-opt" id="vopt-density-comfortable" onclick="setViewPref('density','comfortable');event.stopPropagation()">○ Comfortable</div>
|
||
<div class="filter-sep-line"></div>
|
||
<div style="padding:6px 12px 2px;font-size:10px;text-transform:uppercase;letter-spacing:.6px;color:var(--muted)">Sidebar</div>
|
||
<div class="filter-opt" id="vopt-sidebar-expanded" onclick="setViewPref('sidebarMode','expanded');event.stopPropagation()">✓ Pinned (always visible)</div>
|
||
<div class="filter-opt" id="vopt-sidebar-collapsed" onclick="setViewPref('sidebarMode','collapsed');event.stopPropagation()">○ Minimized</div>
|
||
<div class="filter-opt" id="vopt-sidebar-auto" onclick="setViewPref('sidebarMode','auto');event.stopPropagation()">○ Auto-hide (peek on hover)</div>
|
||
</div>
|
||
</div>
|
||
<div class="filter-dropdown" id="labels-dropdown">
|
||
<button class="filter-dropdown-btn" id="labels-dropdown-btn" title="Labels" onclick="toggleLabelsDropdown(event)">
|
||
<svg width="13" height="13" viewBox="0 0 24 24" fill="currentColor"><path d="M17.63 5.84C17.27 5.33 16.67 5 16 5L5 5.01C3.9 5.01 3 5.9 3 7v10c0 1.1.9 1.99 2 1.99L16 19c.67 0 1.27-.33 1.63-.84L22 12l-4.37-6.16z"/></svg>
|
||
<span>Labels</span>
|
||
</button>
|
||
<div class="filter-dropdown-menu" id="labels-dropdown-menu" style="display:none;min-width:210px"></div>
|
||
</div>
|
||
<div class="filter-dropdown" id="filter-dropdown">
|
||
<button class="filter-dropdown-btn" id="filter-dropdown-btn" title="Filter & sort" onclick="var m=document.getElementById('filter-dropdown-menu');m.style.display=m.style.display==='block'?'none':'block';event.stopPropagation()">
|
||
<svg width="13" height="13" viewBox="0 0 24 24" fill="currentColor"><path d="M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z"/></svg>
|
||
<span id="filter-label">Filter</span>
|
||
</button>
|
||
<div class="filter-dropdown-menu" id="filter-dropdown-menu" style="display:none">
|
||
<div class="filter-opt" id="fopt-default" onclick="goMailSetFilter('default');event.stopPropagation()">✓ Default order</div>
|
||
<div class="filter-sep-line"></div>
|
||
<div class="filter-opt" id="fopt-unread" onclick="goMailSetFilter('unread');event.stopPropagation()">○ Unread only</div>
|
||
<div class="filter-opt" id="fopt-attachment" onclick="goMailSetFilter('attachment');event.stopPropagation()">○ 📎 Has attachment</div>
|
||
<div class="filter-sep-line"></div>
|
||
<div class="filter-opt" id="fopt-date-desc" onclick="goMailSetFilter('date-desc');event.stopPropagation()">○ Newest first</div>
|
||
<div class="filter-opt" id="fopt-date-asc" onclick="goMailSetFilter('date-asc');event.stopPropagation()">○ Oldest first</div>
|
||
<div class="filter-opt" id="fopt-size-desc" onclick="goMailSetFilter('size-desc');event.stopPropagation()">○ Largest first</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="search-bar">
|
||
<div style="display:flex;gap:6px;align-items:center">
|
||
<div class="search-wrap" style="flex:1">
|
||
<svg viewBox="0 0 24 24"><path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/></svg>
|
||
<input class="search-input" type="text" id="search-input" placeholder="Search emails..." oninput="handleSearch(this.value)" onkeydown="if(event.key==='Enter')applySearchFilters()">
|
||
</div>
|
||
<button class="filter-dropdown-btn" id="search-filters-btn" title="Search filters" onclick="toggleSearchFilters(event)" style="flex-shrink:0">
|
||
<svg width="13" height="13" viewBox="0 0 24 24" fill="currentColor"><path d="M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z"/></svg>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<div class="message-list" id="message-list">
|
||
<div class="spinner" style="margin-top:60px"></div>
|
||
</div>
|
||
</div>
|
||
<div class="panel-resize-handle" id="panel-resize-handle" title="Drag to resize"></div>
|
||
|
||
<!-- Message detail -->
|
||
<main class="message-detail" id="message-detail">
|
||
<div class="no-message">
|
||
<svg viewBox="0 0 24 24"><path d="M20 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 4l-8 5-8-5V6l8 5 8-5v2z"/></svg>
|
||
<h3>Select a message</h3>
|
||
<p>Choose a message from the list to read it</p>
|
||
</div>
|
||
</main>
|
||
</div>
|
||
|
||
<!-- ── Contacts panel ──────────────────────────────────────────────────── -->
|
||
<div id="contacts-panel" style="display:none;flex:1;flex-direction:column;overflow:hidden;background:var(--bg)">
|
||
<div class="panel-header" style="padding:14px 18px 10px;border-bottom:1px solid var(--border);display:flex;align-items:center;gap:10px;flex-shrink:0">
|
||
<span style="font-family:'DM Serif Display',serif;font-size:17px;flex:1">Contacts</span>
|
||
<input id="contacts-search" type="search" placeholder="Search contacts…" oninput="filterContacts(this.value)"
|
||
style="padding:5px 10px;border:1px solid var(--border2);border-radius:6px;background:var(--surface3);color:var(--text);font-size:13px;width:200px">
|
||
<button class="btn-secondary" onclick="openContactForm()" style="font-size:12px">+ New Contact</button>
|
||
</div>
|
||
<div id="contacts-list" style="flex:1;overflow-y:auto;padding:12px"></div>
|
||
</div>
|
||
|
||
<!-- ── Calendar panel ──────────────────────────────────────────────────── -->
|
||
<div id="calendar-panel" style="display:none;flex:1;flex-direction:column;overflow:hidden;background:var(--bg)">
|
||
<div style="padding:12px 18px 10px;border-bottom:1px solid var(--border);display:flex;align-items:center;gap:8px;flex-shrink:0">
|
||
<button class="icon-btn" onclick="calNav(-1)" title="Previous">‹</button>
|
||
<span id="cal-title" style="font-family:'DM Serif Display',serif;font-size:17px;min-width:200px;text-align:center"></span>
|
||
<button class="icon-btn" onclick="calNav(1)" title="Next">›</button>
|
||
<button class="btn-secondary" onclick="calGoToday()" style="font-size:12px;margin-left:4px">Today</button>
|
||
<div style="margin-left:auto;display:flex;gap:4px">
|
||
<button class="btn-secondary" id="cal-btn-month" onclick="calSetView('month')" style="font-size:12px">Month</button>
|
||
<button class="btn-secondary" id="cal-btn-week" onclick="calSetView('week')" style="font-size:12px">Week</button>
|
||
<button class="btn-secondary" onclick="openEventForm()" style="font-size:12px;background:var(--accent);color:white;border-color:var(--accent)">+ Event</button>
|
||
<button class="icon-btn" onclick="showCalDAVSettings()" title="CalDAV / sharing">
|
||
<svg viewBox="0 0 24 24" width="15" height="15" fill="currentColor"><path d="M18 8h-1V6c0-2.76-2.24-5-5-5S7 3.24 7 6v2H6c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V10c0-1.1-.9-2-2-2zm-6 9c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2zm3.1-9H8.9V6c0-1.71 1.39-3.1 3.1-3.1 1.71 0 3.1 1.39 3.1 3.1v2z"/></svg>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<div id="cal-grid" style="flex:1;overflow-y:auto"></div>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<!-- ── Contact form modal ──────────────────────────────────────────────────── -->
|
||
<div class="modal-overlay" id="contact-modal">
|
||
<div class="modal" style="max-width:480px">
|
||
<h2 id="contact-modal-title">New Contact</h2>
|
||
<div class="modal-field"><label>Name</label><input id="cf-name" type="text" placeholder="Full name"></div>
|
||
<div class="modal-field"><label>Email</label><input id="cf-email" type="email" placeholder="email@example.com"></div>
|
||
<div class="modal-field"><label>Phone</label><input id="cf-phone" type="tel" placeholder="+1 555 000 0000"></div>
|
||
<div class="modal-field"><label>Company</label><input id="cf-company" type="text" placeholder="Company name"></div>
|
||
<div class="modal-field"><label>Notes</label><textarea id="cf-notes" rows="3" style="width:100%;resize:vertical;padding:8px;background:var(--surface3);border:1px solid var(--border2);border-radius:6px;color:var(--text);font-family:'DM Sans',sans-serif;font-size:13px"></textarea></div>
|
||
<div class="modal-actions">
|
||
<button class="modal-cancel" onclick="closeModal('contact-modal')">Cancel</button>
|
||
<button id="cf-delete-btn" class="btn-secondary" style="color:var(--danger);display:none" onclick="deleteContact()">Delete</button>
|
||
<button class="modal-submit" onclick="saveContact()">Save</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ── Event form modal ──────────────────────────────────────────────────── -->
|
||
<div class="modal-overlay" id="event-modal">
|
||
<div class="modal" style="max-width:520px">
|
||
<h2 id="event-modal-title">New Event</h2>
|
||
<div class="modal-field"><label>Title</label><input id="ev-title" type="text" placeholder="Event title"></div>
|
||
<div class="modal-row">
|
||
<div class="modal-field"><label>Start</label><input id="ev-start" type="datetime-local"></div>
|
||
<div class="modal-field"><label>End</label><input id="ev-end" type="datetime-local"></div>
|
||
</div>
|
||
<div class="modal-field" style="flex-direction:row;align-items:center;gap:8px">
|
||
<input id="ev-allday" type="checkbox" style="width:auto">
|
||
<label for="ev-allday" style="font-weight:normal;color:var(--text2)">All day</label>
|
||
</div>
|
||
<div class="modal-field"><label>Location</label><input id="ev-location" type="text" placeholder="Location or video link"></div>
|
||
<div class="modal-field"><label>Description</label><textarea id="ev-desc" rows="3" style="width:100%;resize:vertical;padding:8px;background:var(--surface3);border:1px solid var(--border2);border-radius:6px;color:var(--text);font-family:'DM Sans',sans-serif;font-size:13px"></textarea></div>
|
||
<div class="modal-field"><label>Color</label>
|
||
<div style="display:flex;gap:6px" id="ev-colors">
|
||
<span data-color="#0078D4" onclick="selectEvColor(this)" style="width:22px;height:22px;border-radius:50%;background:#0078D4;cursor:pointer;border:2px solid transparent"></span>
|
||
<span data-color="#EA4335" onclick="selectEvColor(this)" style="width:22px;height:22px;border-radius:50%;background:#EA4335;cursor:pointer;border:2px solid transparent"></span>
|
||
<span data-color="#34A853" onclick="selectEvColor(this)" style="width:22px;height:22px;border-radius:50%;background:#34A853;cursor:pointer;border:2px solid transparent"></span>
|
||
<span data-color="#FBBC04" onclick="selectEvColor(this)" style="width:22px;height:22px;border-radius:50%;background:#FBBC04;cursor:pointer;border:2px solid transparent"></span>
|
||
<span data-color="#9C27B0" onclick="selectEvColor(this)" style="width:22px;height:22px;border-radius:50%;background:#9C27B0;cursor:pointer;border:2px solid transparent"></span>
|
||
<span data-color="#FF6D00" onclick="selectEvColor(this)" style="width:22px;height:22px;border-radius:50%;background:#FF6D00;cursor:pointer;border:2px solid transparent"></span>
|
||
</div>
|
||
</div>
|
||
<div class="modal-actions">
|
||
<button class="modal-cancel" onclick="closeModal('event-modal')">Cancel</button>
|
||
<button id="ev-delete-btn" class="btn-secondary" style="color:var(--danger);display:none" onclick="deleteEvent()">Delete</button>
|
||
<button class="modal-submit" onclick="saveEvent()">Save</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ── CalDAV settings modal ──────────────────────────────────────────────── -->
|
||
<div class="modal-overlay" id="caldav-modal">
|
||
<div class="modal" style="max-width:560px">
|
||
<h2>CalDAV / Calendar Sharing</h2>
|
||
<p style="font-size:13px;color:var(--text2);margin-bottom:14px">
|
||
Subscribe to your GoWebMail calendar from any CalDAV client (Apple Calendar, Thunderbird, etc.) using a token URL. Tokens give read-only calendar access — no password needed.
|
||
</p>
|
||
<div id="caldav-tokens-list" style="margin-bottom:14px"></div>
|
||
<div style="display:flex;gap:8px;align-items:center">
|
||
<input id="caldav-label" type="text" placeholder="Token label (e.g. iPhone)" style="flex:1;padding:7px 10px;background:var(--surface3);border:1px solid var(--border2);border-radius:6px;color:var(--text);font-size:13px">
|
||
<button class="btn-secondary" onclick="createCalDAVToken()" style="white-space:nowrap">Generate Token</button>
|
||
</div>
|
||
<div class="modal-actions" style="margin-top:16px">
|
||
<button class="modal-cancel" onclick="closeModal('caldav-modal')">Close</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ── Draggable Compose dialog ───────────────────────────────────────────── -->
|
||
<div class="compose-dialog" id="compose-dialog">
|
||
<div class="compose-dialog-header" id="compose-drag-handle">
|
||
<span class="compose-title" id="compose-title">New Message</span>
|
||
<div style="display:flex;align-items:center;gap:2px">
|
||
<button class="compose-close" onclick="minimizeCompose()" title="Minimise">–</button>
|
||
<button class="compose-close" onclick="closeCompose()" title="Close">×</button>
|
||
</div>
|
||
</div>
|
||
<div class="compose-body-wrap" id="compose-body-wrap">
|
||
<div class="compose-field"><label>From</label><select id="compose-from" onchange="onComposeFromChange()"></select></div>
|
||
<div class="compose-field compose-tag-field"><label>To</label><div id="compose-to" class="tag-container"></div></div>
|
||
<div class="compose-field compose-tag-field" id="cc-row" style="display:none"><label>CC</label><div id="compose-cc-tags" class="tag-container"></div></div>
|
||
<div class="compose-field compose-tag-field" id="bcc-row" style="display:none"><label>BCC</label><div id="compose-bcc-tags" class="tag-container"></div></div>
|
||
<div class="compose-field"><label>Subject</label><input type="text" id="compose-subject" oninput="S.draftDirty=true"></div>
|
||
<div class="compose-toolbar">
|
||
<button class="fmt-btn" title="Bold" onclick="execFmt('bold')"><b>B</b></button>
|
||
<button class="fmt-btn" title="Italic" onclick="execFmt('italic')"><i>I</i></button>
|
||
<button class="fmt-btn" title="Underline" onclick="execFmt('underline')"><u>U</u></button>
|
||
<span class="fmt-sep"></span>
|
||
<button class="fmt-btn" title="Bullets" onclick="execFmt('insertUnorderedList')">•—</button>
|
||
<button class="fmt-btn" title="Numbers" onclick="execFmt('insertOrderedList')">1—</button>
|
||
<span class="fmt-sep"></span>
|
||
<button class="fmt-btn" title="Link" onclick="insertLink()">🔗</button>
|
||
<button class="fmt-btn" title="Clear format" onclick="execFmt('removeFormat')">T⃗</button>
|
||
</div>
|
||
<div id="compose-editor" contenteditable="true" class="compose-editor" placeholder="Write your message..."></div>
|
||
<div id="compose-attach-list" class="compose-attach-list"></div>
|
||
<div class="compose-footer">
|
||
<button class="send-btn" id="send-btn" onclick="sendMessage()">Send</button>
|
||
<div style="display:flex;gap:6px;margin-left:4px">
|
||
<button class="btn-secondary" style="font-size:12px" onclick="showCCRow()">+CC</button>
|
||
<button class="btn-secondary" style="font-size:12px" onclick="showBCCRow()">+BCC</button>
|
||
<button class="btn-secondary" style="font-size:12px" onclick="triggerAttach()">📎 Attach</button>
|
||
<button class="btn-secondary" style="font-size:12px" onclick="saveDraft()">✎ Draft</button>
|
||
</div>
|
||
<input type="file" id="compose-attach-input" multiple style="display:none" onchange="handleAttachFiles(this)">
|
||
</div>
|
||
</div>
|
||
<div class="compose-resize" data-dir="e"></div>
|
||
<div class="compose-resize" data-dir="s"></div>
|
||
<div class="compose-resize" data-dir="se"></div>
|
||
<div class="compose-resize" data-dir="w"></div>
|
||
<div class="compose-resize" data-dir="sw"></div>
|
||
<div class="compose-resize" data-dir="n"></div>
|
||
<div class="compose-resize" data-dir="ne"></div>
|
||
<div class="compose-resize" data-dir="nw"></div>
|
||
</div>
|
||
<!-- Minimised pill (shown when user clicks – on header) -->
|
||
<div class="compose-minimised" id="compose-minimised" onclick="restoreCompose()">
|
||
<svg width="13" height="13" viewBox="0 0 24 24" fill="currentColor"><path d="M20 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 4l-8 5-8-5V6l8 5 8-5v2z"/></svg>
|
||
<span id="compose-minimised-label">New Message</span>
|
||
</div>
|
||
|
||
<!-- ── Inline confirm (replaces browser confirm()) ───────────────────────── -->
|
||
<div class="inline-confirm" id="inline-confirm">
|
||
<p id="inline-confirm-msg" style="margin:0 0 14px;font-size:13px;line-height:1.5"></p>
|
||
<div style="display:flex;gap:8px;justify-content:flex-end">
|
||
<button class="btn-secondary" style="font-size:12px" id="inline-confirm-cancel">Cancel</button>
|
||
<button class="btn-danger" style="font-size:12px" id="inline-confirm-ok">Confirm</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ── Inline prompt (replaces browser prompt()) ─────────────────────────── -->
|
||
<div class="inline-confirm" id="inline-prompt">
|
||
<p id="inline-prompt-msg" style="margin:0 0 10px;font-size:13px;line-height:1.5"></p>
|
||
<div class="modal-field">
|
||
<input type="text" id="inline-prompt-input"
|
||
onkeydown="if(event.key==='Enter'){document.getElementById('inline-prompt-ok').click();}else if(event.key==='Escape'){document.getElementById('inline-prompt-cancel').click();}">
|
||
</div>
|
||
<div style="display:flex;gap:8px;justify-content:flex-end">
|
||
<button class="btn-secondary" style="font-size:12px" id="inline-prompt-cancel">Cancel</button>
|
||
<button class="btn-primary" style="font-size:12px" id="inline-prompt-ok">Create</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ── Draft close confirm (save / delete / keep editing) ─────────────────── -->
|
||
<div class="inline-confirm" id="draft-close-confirm">
|
||
<p style="margin:0 0 14px;font-size:13px;line-height:1.5">Save this message as a draft before closing?</p>
|
||
<div style="display:flex;gap:8px;justify-content:flex-end;flex-wrap:wrap">
|
||
<button class="btn-secondary" style="font-size:12px" id="draft-close-cancel">Keep editing</button>
|
||
<button class="btn-danger" style="font-size:12px" id="draft-close-delete">Delete draft</button>
|
||
<button class="btn-primary" style="font-size:12px" id="draft-close-save">Save draft</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ── Add Account Modal ──────────────────────────────────────────────────── -->
|
||
<div class="modal-overlay" id="add-account-modal">
|
||
<div class="modal">
|
||
<h2>Connect an account</h2>
|
||
<p>Connect Gmail or Outlook via OAuth, or any email via IMAP/SMTP.</p>
|
||
<div class="provider-btns">
|
||
<button class="provider-btn" id="btn-gmail" onclick="connectOAuth('gmail')">
|
||
<svg viewBox="0 0 24 24" width="20" height="20"><path fill="#EA4335" d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"/><path fill="#4285F4" d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"/><path fill="#FBBC05" d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"/><path fill="#EA4335" d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"/></svg>
|
||
Gmail
|
||
</button>
|
||
<button class="provider-btn" id="btn-outlook" onclick="connectOAuth('outlook')">
|
||
<!-- Microsoft 365 icon -->
|
||
<svg viewBox="0 0 24 24" width="20" height="20" xmlns="http://www.w3.org/2000/svg">
|
||
<path fill="#EA3E23" d="M11.4 4H4v7.4h7.4V4z"/>
|
||
<path fill="#0364B8" d="M11.4 12.6H4V20h7.4v-7.4z"/>
|
||
<path fill="#0078D4" d="M20 4h-7.4v7.4H20V4z"/>
|
||
<path fill="#28A8E8" d="M20 12.6h-7.4V20H20v-7.4z"/>
|
||
</svg>
|
||
Microsoft 365
|
||
</button>
|
||
<button class="provider-btn" id="btn-outlook-personal" onclick="connectOAuth('outlook_personal')">
|
||
<!-- Outlook icon (blue envelope) -->
|
||
<svg viewBox="0 0 24 24" width="20" height="20" xmlns="http://www.w3.org/2000/svg">
|
||
<rect width="24" height="24" rx="3" fill="#0078D4"/>
|
||
<path fill="white" d="M6 7h12v10H6z" opacity=".2"/>
|
||
<path fill="white" d="M6 7l6 5 6-5H6zm0 1.5V17h12V8.5l-6 5-6-5z"/>
|
||
</svg>
|
||
Outlook Personal
|
||
</button>
|
||
</div>
|
||
<div class="modal-divider"><span>or add IMAP account</span></div>
|
||
<div class="modal-field"><label>Email Address</label>
|
||
<div style="display:flex;gap:8px;flex:1">
|
||
<input type="email" id="imap-email" placeholder="you@example.com" style="flex:1">
|
||
<button class="btn-secondary" id="detect-btn" onclick="detectMailSettings()" style="white-space:nowrap;font-size:12px">Auto-detect</button>
|
||
</div>
|
||
</div>
|
||
<div class="modal-field"><label>Display Name</label><input type="text" id="imap-name" placeholder="Your Name"></div>
|
||
<div class="modal-field"><label>Password / App Password</label><input type="password" id="imap-password"></div>
|
||
<div class="modal-field" style="display:flex;align-items:center;gap:8px;flex-direction:row">
|
||
<input type="checkbox" id="use-jmap" onchange="toggleJMAPFields()" style="width:auto;flex:none">
|
||
<label for="use-jmap" style="margin:0;font-weight:400">Connect via JMAP instead of IMAP/SMTP</label>
|
||
</div>
|
||
<div id="imap-hint" style="font-size:11px;color:var(--muted);padding:0 0 8px;line-height:1.6">
|
||
Common ports — IMAP: <strong>993</strong> TLS/SSL, <strong>143</strong> STARTTLS/Plain ·
|
||
SMTP: <strong>587</strong> STARTTLS, <strong>465</strong> TLS/SSL, <strong>25</strong> Plain
|
||
</div>
|
||
<div class="modal-row">
|
||
<div class="modal-field"><label id="imap-host-label">IMAP Host</label><input type="text" id="imap-host" placeholder="imap.example.com"></div>
|
||
<div class="modal-field" id="imap-port-field"><label>IMAP Port</label><input type="number" id="imap-port" value="993"></div>
|
||
</div>
|
||
<div class="modal-row" id="smtp-fields">
|
||
<div class="modal-field"><label>SMTP Host</label><input type="text" id="smtp-host" placeholder="smtp.example.com"></div>
|
||
<div class="modal-field"><label>SMTP Port</label><input type="number" id="smtp-port" value="587"></div>
|
||
</div>
|
||
<div class="modal-divider"><span>optional — sync calendar & contacts</span></div>
|
||
<div class="modal-field"><label>CalDAV URL</label><input type="text" id="imap-caldav-url" placeholder="https://mail.example.com/dav/calendars/user@example.com/default"></div>
|
||
<div class="modal-field"><label>CardDAV URL</label><input type="text" id="imap-carddav-url" placeholder="https://mail.example.com/dav/addressbooks/user@example.com/default"></div>
|
||
<div class="test-result" id="test-result"></div>
|
||
<div class="modal-actions">
|
||
<button class="modal-cancel" onclick="closeModal('add-account-modal')">Cancel</button>
|
||
<button class="btn-secondary" onclick="testNewConnection()" id="test-btn">Test Connection</button>
|
||
<button class="modal-submit" onclick="addIMAPAccount()" id="save-acct-btn">Connect</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ── Label Editor Modal (create/rename/recolor) ────────────────────────────── -->
|
||
<div class="modal-overlay" id="label-editor-modal">
|
||
<div class="modal" style="max-width:360px">
|
||
<h2 id="label-editor-title">New Label</h2>
|
||
<input type="hidden" id="label-editor-id">
|
||
<div class="modal-field"><label>Name</label><input type="text" id="label-editor-name" maxlength="40"></div>
|
||
<div class="modal-field">
|
||
<label>Color</label>
|
||
<div class="label-swatches" id="label-editor-swatches"></div>
|
||
<input type="color" id="label-editor-custom-color" onchange="pickLabelColor(this.value)" style="width:40px;height:28px;padding:0;border:1px solid var(--border);border-radius:6px;background:none;cursor:pointer">
|
||
</div>
|
||
<div class="modal-actions">
|
||
<button class="modal-cancel" onclick="closeModal('label-editor-modal')">Cancel</button>
|
||
<button class="modal-submit" onclick="saveLabelEditor()">Save</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ── Edit Account Modal ─────────────────────────────────────────────────── -->
|
||
<div class="modal-overlay" id="edit-account-modal">
|
||
<div class="modal">
|
||
<h2>Account Settings</h2>
|
||
<p id="edit-account-email" style="font-weight:500;color:var(--text);margin-bottom:16px"></p>
|
||
<input type="hidden" id="edit-account-id">
|
||
<div class="modal-field"><label>Display Name</label><input type="text" id="edit-name"></div>
|
||
|
||
<!-- OAuth reconnect — shown only for gmail/outlook accounts -->
|
||
<div id="edit-oauth-section" style="display:none">
|
||
<div id="edit-oauth-expired-warning" style="display:none;background:rgba(239,68,68,.12);border:1px solid rgba(239,68,68,.35);border-radius:8px;padding:10px 14px;margin-bottom:10px;font-size:13px;color:#f87171">
|
||
⚠️ Access token has expired — sync and send will fail until you reconnect.
|
||
</div>
|
||
<div style="background:var(--bg);border:1px solid var(--border);border-radius:8px;padding:14px 16px;margin-bottom:4px">
|
||
<div style="font-size:13px;color:var(--muted);margin-bottom:10px">This account connects via <strong id="edit-oauth-provider-label"></strong> OAuth. To update permissions or fix an expired token, reconnect below.</div>
|
||
<button class="btn-secondary" id="edit-oauth-reconnect-btn" style="width:100%">🔗 Reconnect with <span id="edit-oauth-provider-label-btn"></span></button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- IMAP/SMTP credentials (hidden for OAuth accounts) -->
|
||
<div id="edit-creds-section">
|
||
<div class="modal-field"><label>New Password (leave blank to keep current)</label><input type="password" id="edit-password"></div>
|
||
<div class="modal-row">
|
||
<div class="modal-field"><label id="edit-imap-host-label">IMAP Host</label><input type="text" id="edit-imap-host"></div>
|
||
<div class="modal-field" id="edit-imap-port-field"><label>IMAP Port</label><input type="number" id="edit-imap-port"></div>
|
||
</div>
|
||
<div class="modal-row" id="edit-smtp-fields">
|
||
<div class="modal-field"><label>SMTP Host</label><input type="text" id="edit-smtp-host"></div>
|
||
<div class="modal-field"><label>SMTP Port</label><input type="number" id="edit-smtp-port"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="settings-group-title" style="margin:16px 0 8px">Calendar & Contacts (optional)</div>
|
||
<div class="modal-field"><label>CalDAV URL</label><input type="text" id="edit-caldav-url" placeholder="leave blank to disable"></div>
|
||
<div class="modal-field"><label>CardDAV URL</label><input type="text" id="edit-carddav-url" placeholder="leave blank to disable"></div>
|
||
|
||
<div class="settings-group-title" style="margin:16px 0 8px">Sync Settings</div>
|
||
<div class="modal-field">
|
||
<label>Email history to sync</label>
|
||
<select id="edit-sync-mode" onchange="toggleSyncDaysField()" style="padding:8px 10px;background:var(--bg);border:1px solid var(--border);border-radius:6px;color:var(--text);font-size:13px;outline:none">
|
||
<option value="preset-30">Last 1 month</option>
|
||
<option value="preset-90">Last 3 months</option>
|
||
<option value="preset-180">Last 6 months</option>
|
||
<option value="preset-365">Last 1 year</option>
|
||
<option value="preset-730">Last 2 years</option>
|
||
<option value="preset-1825">Last 5 years</option>
|
||
<option value="all" selected>All emails (full mailbox)</option>
|
||
<option value="days">Custom (days)</option>
|
||
</select>
|
||
</div>
|
||
<div class="modal-row" id="edit-sync-days-row" style="display:none">
|
||
<div class="modal-field"><label>Custom days to fetch</label><input type="number" id="edit-sync-days" value="30" min="1" max="36500"></div>
|
||
</div>
|
||
<div id="edit-conn-result" class="test-result" style="display:none"></div>
|
||
<div id="edit-last-error" style="display:none" class="alert error"></div>
|
||
<div class="settings-group-title" style="margin:16px 0 8px">Hidden Folders</div>
|
||
<div id="edit-hidden-folders" style="font-size:12px;color:var(--muted)">Loading…</div>
|
||
<div class="modal-actions">
|
||
<button class="modal-cancel" onclick="closeModal('edit-account-modal')">Cancel</button>
|
||
<button class="btn-secondary" id="edit-test-btn" onclick="testEditConnection()">Test Connection</button>
|
||
<button class="modal-submit" onclick="saveAccountEdit()">Save</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ── Settings Modal ─────────────────────────────────────────────────────── -->
|
||
<div class="modal-overlay" id="settings-modal">
|
||
<div class="modal" style="width:820px;max-width:95vw;height:640px;max-height:90vh;padding:0;display:flex;flex-direction:column">
|
||
<div style="display:flex;align-items:center;justify-content:space-between;padding:22px 24px 16px">
|
||
<h2 style="margin-bottom:0">Settings</h2>
|
||
<button onclick="closeModal('settings-modal')" class="icon-btn"><svg viewBox="0 0 24 24"><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg></button>
|
||
</div>
|
||
<div style="display:flex;align-items:stretch;min-height:0;flex:1;border-top:1px solid var(--border)">
|
||
<div class="settings-nav">
|
||
<button data-tab="accounts" class="active" onclick="showSettingsTab('accounts')">Accounts</button>
|
||
<button data-tab="account" onclick="showSettingsTab('account')">Profile</button>
|
||
<button data-tab="rules" onclick="showSettingsTab('rules')">Rules</button>
|
||
<button data-tab="signatures" onclick="showSettingsTab('signatures')">Signatures</button>
|
||
<button data-tab="certs" onclick="showSettingsTab('certs')">Certificates</button>
|
||
</div>
|
||
<div style="flex:1;min-width:0;overflow-y:auto;padding:20px 24px">
|
||
|
||
<div class="settings-panel active" data-tab="accounts">
|
||
<div class="settings-group">
|
||
<div class="settings-group-title">Connected mailboxes</div>
|
||
<div style="font-size:12px;color:var(--muted);margin-bottom:10px">Manage sync, credentials, CalDAV/CardDAV and per-account settings for each connected mailbox.</div>
|
||
<div id="settings-accounts-list"></div>
|
||
<button class="accounts-add-btn" onclick="openAddAccountModal()">
|
||
<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor"><path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/></svg>
|
||
Connect new account
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="settings-panel" data-tab="account">
|
||
<div class="settings-group">
|
||
<div class="settings-group-title">Profile</div>
|
||
<div class="modal-field">
|
||
<label>Username</label>
|
||
<div style="display:flex;gap:8px">
|
||
<input type="text" id="profile-username" placeholder="New username" style="flex:1">
|
||
<button class="btn-primary" onclick="updateProfile('username')">Save</button>
|
||
</div>
|
||
</div>
|
||
<div class="modal-field">
|
||
<label>Email Address</label>
|
||
<div style="display:flex;gap:8px">
|
||
<input type="email" id="profile-email" placeholder="New email address" style="flex:1">
|
||
<button class="btn-primary" onclick="updateProfile('email')">Save</button>
|
||
</div>
|
||
</div>
|
||
<div class="modal-field">
|
||
<label>Current Password <span style="color:var(--muted);font-size:11px">(required to confirm changes)</span></label>
|
||
<input type="password" id="profile-confirm-pw" placeholder="Enter your current password">
|
||
</div>
|
||
</div>
|
||
|
||
<div class="settings-group">
|
||
<div class="settings-group-title">Email Sync</div>
|
||
<div style="font-size:13px;color:var(--muted);margin-bottom:12px">How often to automatically check all your accounts for new mail.</div>
|
||
<div style="display:flex;gap:10px;align-items:center">
|
||
<select id="sync-interval-select" style="flex:1;padding:8px 10px;background:var(--bg);border:1px solid var(--border);border-radius:6px;color:var(--text);font-family:'DM Sans',sans-serif;font-size:13px;outline:none">
|
||
<option value="0">Manual only</option>
|
||
<option value="1">Every 1 minute</option>
|
||
<option value="5">Every 5 minutes</option>
|
||
<option value="10">Every 10 minutes</option>
|
||
<option value="15">Every 15 minutes (default)</option>
|
||
<option value="30">Every 30 minutes</option>
|
||
<option value="60">Every 60 minutes</option>
|
||
</select>
|
||
<button class="btn-primary" onclick="saveSyncInterval()">Save</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="settings-group">
|
||
<div class="settings-group-title">Change Password</div>
|
||
<div class="modal-field"><label>Current Password</label><input type="password" id="cur-pw"></div>
|
||
<div class="modal-field"><label>New Password</label><input type="password" id="new-pw" placeholder="Min. 8 characters"></div>
|
||
<button class="btn-primary" onclick="changePassword()">Update Password</button>
|
||
</div>
|
||
|
||
<div class="settings-group">
|
||
<div class="settings-group-title" style="display:flex;align-items:center;gap:10px">
|
||
Two-Factor Authentication <span id="mfa-badge"></span>
|
||
</div>
|
||
<div id="mfa-panel">Loading...</div>
|
||
</div>
|
||
|
||
<div class="settings-group">
|
||
<div class="settings-group-title">IP Access Rules</div>
|
||
<div style="font-size:13px;color:var(--muted);margin-bottom:14px">
|
||
Control which IP addresses can access your account. This overrides global brute-force settings for your account only.
|
||
</div>
|
||
<div class="modal-field">
|
||
<label>Mode</label>
|
||
<select id="ip-rule-mode" onchange="toggleIPRuleHelp()" style="width:100%;padding:8px 10px;background:var(--bg);border:1px solid var(--border);border-radius:6px;color:var(--text);font-family:'DM Sans',sans-serif;font-size:13px;outline:none">
|
||
<option value="disabled">Disabled — use global settings</option>
|
||
<option value="brute_skip">Skip brute-force check — listed IPs bypass lockout</option>
|
||
<option value="allow_only">Allow only — only listed IPs can log in</option>
|
||
</select>
|
||
</div>
|
||
<div id="ip-rule-help" style="font-size:12px;color:var(--muted);margin-bottom:10px;display:none"></div>
|
||
<div class="modal-field" id="ip-rule-list-field">
|
||
<label>Allowed IPs <span style="color:var(--muted);font-size:11px">(comma-separated)</span></label>
|
||
<input type="text" id="ip-rule-list" placeholder="e.g. 192.168.1.10, 10.0.0.5">
|
||
</div>
|
||
<button class="btn-primary" onclick="saveIPRules()">Save IP Rules</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="settings-panel" data-tab="rules">
|
||
<div class="settings-group">
|
||
<div class="settings-group-title">Rules apply to</div>
|
||
<select id="rules-account-select" onchange="loadRules()" style="width:100%;padding:8px 10px;background:var(--bg);border:1px solid var(--border);border-radius:6px;color:var(--text);font-family:'DM Sans',sans-serif;font-size:13px;outline:none"></select>
|
||
</div>
|
||
<div class="settings-group">
|
||
<div class="settings-group-title">Add Rule</div>
|
||
<div style="font-size:12px;color:var(--muted);margin-bottom:12px">Rules run in priority order (lowest first) against newly-synced mail; the first match wins.</div>
|
||
<div class="modal-field"><label>Rule name</label><input type="text" id="rule-name" placeholder="e.g. Invoices to Accounting"></div>
|
||
<div style="display:flex;gap:8px">
|
||
<div class="modal-field" style="flex:1"><label>Priority</label><input type="number" id="rule-priority" value="0"></div>
|
||
<div class="modal-field" style="flex:1"><label>Match</label>
|
||
<select id="rule-match-type"><option value="all">ALL of the following (AND)</option><option value="any">ANY of the following (OR)</option></select>
|
||
</div>
|
||
</div>
|
||
<div id="rule-conditions"></div>
|
||
<button class="btn-secondary" style="margin-bottom:14px" onclick="addRuleConditionRow()">+ Add condition</button>
|
||
<div style="display:flex;gap:8px;align-items:flex-end;flex-wrap:wrap">
|
||
<div class="modal-field" style="flex:1;min-width:140px"><label>Then</label>
|
||
<select id="rule-action" onchange="updateRuleActionFields()">
|
||
<option value="move_to_folder">Move to folder</option>
|
||
<option value="mark_as_spam">Mark as Junk</option>
|
||
<option value="delete">Delete</option>
|
||
<option value="mark_read">Mark as read</option>
|
||
<option value="forward">Forward to...</option>
|
||
<option value="auto_reply">Send auto-reply</option>
|
||
</select>
|
||
</div>
|
||
<div class="modal-field" style="flex:1;min-width:160px" id="rule-action-value-field"><label>Folder name</label><input type="text" id="rule-action-value" placeholder="folder name"></div>
|
||
</div>
|
||
<div class="modal-field" id="rule-autoreply-body-field" style="display:none"><label>Auto-reply body</label><textarea id="rule-autoreply-body" rows="3" style="width:100%"></textarea></div>
|
||
<button class="btn-primary" onclick="saveRule()">Add Rule</button>
|
||
</div>
|
||
<div class="settings-group">
|
||
<div class="settings-group-title">Existing Rules</div>
|
||
<div id="rules-list"><p style="color:var(--muted);font-size:13px">No rules yet.</p></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="settings-panel" data-tab="signatures">
|
||
<div class="settings-group">
|
||
<div class="settings-group-title">Add Signature</div>
|
||
<div class="modal-field"><label>Name</label><input type="text" id="sig-name" placeholder="e.g. Work"></div>
|
||
<div class="modal-field"><label>Content (HTML)</label><textarea id="sig-content" rows="4" style="width:100%" placeholder="Best regards, Your Name"></textarea></div>
|
||
<button class="btn-primary" onclick="saveSignature()">Add Signature</button>
|
||
</div>
|
||
<div class="settings-group">
|
||
<div class="settings-group-title">Your Signatures</div>
|
||
<div id="signatures-list"><p style="color:var(--muted);font-size:13px">No signatures yet.</p></div>
|
||
</div>
|
||
<div class="settings-group">
|
||
<div class="settings-group-title">Defaults per account</div>
|
||
<select id="sig-defaults-account-select" onchange="renderSignatureDefaultsForm()" style="width:100%;margin-bottom:10px;padding:8px 10px;background:var(--bg);border:1px solid var(--border);border-radius:6px;color:var(--text);font-family:'DM Sans',sans-serif;font-size:13px;outline:none"></select>
|
||
<div id="sig-defaults-form"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="settings-panel" data-tab="certs">
|
||
<div class="settings-group">
|
||
<div class="settings-group-title">Certificates apply to</div>
|
||
<select id="certs-account-select" onchange="loadCerts()" style="width:100%;padding:8px 10px;background:var(--bg);border:1px solid var(--border);border-radius:6px;color:var(--text);font-family:'DM Sans',sans-serif;font-size:13px;outline:none"></select>
|
||
<div style="font-size:12px;color:var(--muted);margin-top:10px">S/MIME certificates <b>sign</b> outgoing mail. PGP keys <b>encrypt</b> it. A message can use either, both, or neither.</div>
|
||
</div>
|
||
<div class="settings-group">
|
||
<div class="settings-group-title">S/MIME — for signing</div>
|
||
<div id="smime-identity-list"></div>
|
||
<div style="display:flex;gap:8px;flex-wrap:wrap;margin:10px 0">
|
||
<button class="btn-primary" onclick="smimeGenerate()">Generate Self-Signed Certificate</button>
|
||
</div>
|
||
<div class="modal-field"><label>Import existing (.p12/.pfx)</label>
|
||
<input type="file" id="smime-import-file" accept=".p12,.pfx">
|
||
<input type="password" id="smime-import-password" placeholder=".p12 export password (if any)" style="margin-top:6px">
|
||
<button class="btn-secondary" style="margin-top:6px" onclick="smimeImport()">Import</button>
|
||
</div>
|
||
<div class="settings-group-title" style="margin-top:16px;font-size:13px">S/MIME contact certificates</div>
|
||
<div style="display:flex;gap:8px;flex-wrap:wrap;margin-bottom:8px">
|
||
<input type="email" id="smime-contact-email" placeholder="someone@example.com" style="flex:1;min-width:160px">
|
||
<input type="file" id="smime-contact-file" accept=".pem,.crt,.cer">
|
||
<button class="btn-secondary" onclick="smimeAddContact()">Add Contact</button>
|
||
</div>
|
||
<div id="smime-contacts-list"></div>
|
||
</div>
|
||
<div class="settings-group">
|
||
<div class="settings-group-title">PGP — for encryption</div>
|
||
<div id="pgp-identity-list"></div>
|
||
<div style="display:flex;gap:8px;flex-wrap:wrap;margin:10px 0">
|
||
<input type="text" id="pgp-gen-label" placeholder="Label (optional)" style="flex:1;min-width:120px">
|
||
<input type="password" id="pgp-gen-pass" placeholder="Passphrase (min 8 chars)" style="flex:1;min-width:140px">
|
||
<input type="password" id="pgp-gen-pass2" placeholder="Confirm passphrase" style="flex:1;min-width:140px">
|
||
<button class="btn-primary" onclick="pgpGenerate()">Generate PGP Key</button>
|
||
</div>
|
||
<div class="modal-field"><label>Import existing (.asc)</label>
|
||
<input type="file" id="pgp-import-file" accept=".asc">
|
||
<input type="password" id="pgp-import-pass" placeholder="The key's passphrase" style="margin-top:6px">
|
||
<button class="btn-secondary" style="margin-top:6px" onclick="pgpImport()">Import</button>
|
||
</div>
|
||
<div class="settings-group-title" style="margin-top:16px;font-size:13px">PGP contact keys</div>
|
||
<div style="display:flex;gap:8px;flex-wrap:wrap;margin-bottom:8px">
|
||
<input type="email" id="pgp-contact-email" placeholder="someone@example.com" style="flex:1;min-width:160px">
|
||
<input type="text" id="pgp-contact-label" placeholder="Label (optional)" style="flex:1;min-width:100px">
|
||
<input type="file" id="pgp-contact-file" accept=".asc">
|
||
<button class="btn-secondary" onclick="pgpAddContact()">Add Contact</button>
|
||
</div>
|
||
<div id="pgp-contacts-list"></div>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Compose split-button dropdown — fixed-position, JS-placed (see toggleComposeDropdown);
|
||
lives outside .sidebar so its overflow:hidden can't clip it -->
|
||
<div id="compose-dropdown" style="display:none;position:fixed;background:var(--surface);border:1px solid var(--border2);border-radius:7px;box-shadow:0 4px 16px rgba(0,0,0,.2);z-index:210;min-width:200px;overflow:hidden">
|
||
<div class="ctx-item" onclick="openCompose();closeComposeDropdown()">✉ New message</div>
|
||
<div class="ctx-item" onclick="window.open('/compose','_blank');closeComposeDropdown()">↗ New message in new tab</div>
|
||
</div>
|
||
|
||
<!-- Search filters popover — fixed-position, JS-placed under the search bar (see
|
||
toggleSearchFilters); same reasoning as #compose-dropdown above. -->
|
||
<div id="search-filters-panel" style="display:none;position:fixed;padding:10px;background:var(--surface2);border:1px solid var(--border2);border-radius:8px;box-shadow:0 8px 28px rgba(0,0,0,.5);z-index:210;max-height:80vh;overflow-y:auto">
|
||
<div class="modal-field" style="margin-bottom:8px">
|
||
<label>Search in mailbox</label>
|
||
<select id="sf-mailbox-scope" style="width:100%;padding:6px 8px;background:var(--bg);border:1px solid var(--border);border-radius:6px;color:var(--text);font-size:12px">
|
||
<option value="">All mailboxes</option>
|
||
</select>
|
||
</div>
|
||
<div class="modal-field" style="margin-bottom:8px">
|
||
<label>Search in</label>
|
||
<select id="sf-scope" style="width:100%;padding:6px 8px;background:var(--bg);border:1px solid var(--border);border-radius:6px;color:var(--text);font-size:12px">
|
||
<option value="all">All fields</option>
|
||
<option value="subject">Subject only</option>
|
||
<option value="body">Body only</option>
|
||
<option value="subject_body">Subject + Body</option>
|
||
</select>
|
||
</div>
|
||
<div class="modal-field" style="margin-bottom:8px">
|
||
<label>Attachment</label>
|
||
<select id="sf-attachment" style="width:100%;padding:6px 8px;background:var(--bg);border:1px solid var(--border);border-radius:6px;color:var(--text);font-size:12px">
|
||
<option value="">Any</option>
|
||
<option value="1">Has attachment</option>
|
||
<option value="0">No attachment</option>
|
||
</select>
|
||
</div>
|
||
<div class="modal-row" style="margin-bottom:8px;gap:8px">
|
||
<div class="modal-field" style="flex:1;margin-bottom:0"><label>From date</label>
|
||
<input type="date" id="sf-date-from" style="width:100%;padding:5px 6px;background:var(--bg);border:1px solid var(--border);border-radius:6px;color:var(--text);font-size:12px">
|
||
</div>
|
||
<div class="modal-field" style="flex:1;margin-bottom:0"><label>To date</label>
|
||
<input type="date" id="sf-date-to" style="width:100%;padding:5px 6px;background:var(--bg);border:1px solid var(--border);border-radius:6px;color:var(--text);font-size:12px">
|
||
</div>
|
||
</div>
|
||
<div class="modal-field" style="margin-bottom:8px">
|
||
<label>Older than (days) <span style="color:var(--muted);font-size:10px">— fills in "To date"</span></label>
|
||
<input type="number" id="sf-older-days" min="0" placeholder="e.g. 30" style="width:100%;padding:5px 6px;background:var(--bg);border:1px solid var(--border);border-radius:6px;color:var(--text);font-size:12px">
|
||
</div>
|
||
<div class="modal-row" style="margin-bottom:10px;gap:8px">
|
||
<div class="modal-field" style="flex:1;margin-bottom:0"><label>Min size (KB)</label>
|
||
<input type="number" id="sf-min-size" min="0" style="width:100%;padding:5px 6px;background:var(--bg);border:1px solid var(--border);border-radius:6px;color:var(--text);font-size:12px">
|
||
</div>
|
||
<div class="modal-field" style="flex:1;margin-bottom:0"><label>Max size (KB)</label>
|
||
<input type="number" id="sf-max-size" min="0" style="width:100%;padding:5px 6px;background:var(--bg);border:1px solid var(--border);border-radius:6px;color:var(--text);font-size:12px">
|
||
</div>
|
||
</div>
|
||
<div style="display:flex;gap:6px;justify-content:flex-end">
|
||
<button class="btn-secondary" style="font-size:12px" onclick="clearSearchFilters()">Clear</button>
|
||
<button class="btn-primary" style="font-size:12px" onclick="applySearchFilters()">Apply</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Context menu -->
|
||
<div class="ctx-menu" id="ctx-menu"></div>
|
||
<div class="toast-container" id="toast-container"></div>
|
||
{{end}}
|
||
|
||
{{define "scripts"}}
|
||
<script src="/static/js/app.js?v=70"></script>
|
||
<script src="/static/js/contacts_calendar.js?v=70"></script>
|
||
{{end}}
|