2026-03-07 06:20:39 +00:00
{{template "base" .}}
{{define "title"}}GoMail{{end}}
{{define "body_class"}}app-page{{end}}
{{define "body"}}
< div class = "app" >
<!-- Sidebar -->
< aside class = "sidebar" >
< div class = "sidebar-header" >
< 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" > GoMail< / span >
< / div >
< button class = "compose-btn" onclick = "openCompose()" > + Compose< / button >
< / 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 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" > Admin< / a >
< / div >
< div class = "footer-actions" >
2026-03-07 15:14:57 +00:00
< button class = "icon-btn" id = "accounts-btn" onclick = "toggleAccountsMenu(event)" title = "Manage accounts" >
< svg viewBox = "0 0 24 24" > < path d = "M16 11c1.66 0 2.99-1.34 2.99-3S17.66 5 16 5c-1.66 0-3 1.34-3 3s1.34 3 3 3zm-8 0c1.66 0 2.99-1.34 2.99-3S9.66 5 8 5C6.34 5 5 6.34 5 8s1.34 3 3 3zm0 2c-2.33 0-7 1.17-7 3.5V19h14v-2.5c0-2.33-4.67-3.5-7-3.5zm8 0c-.29 0-.62.02-.97.05 1.16.84 1.97 1.97 1.97 3.45V19h6v-2.5c0-2.33-4.67-3.5-7-3.5z" / > < / svg >
< / button >
2026-03-07 06:20:39 +00:00
< 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 >
<!-- Message list -->
< div class = "message-list-panel" >
< div class = "panel-header" >
< span class = "panel-title" id = "panel-title" > Unified Inbox< / span >
2026-03-07 16:49:23 +00:00
< div style = "display:flex;align-items:center;gap:6px" >
< span class = "panel-count" id = "panel-count" > < / span >
< 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-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 >
2026-03-07 06:20:39 +00:00
< / div >
< div class = "search-bar" >
< div class = "search-wrap" >
< 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)" >
< / div >
< / div >
< div class = "message-list" id = "message-list" >
< div class = "spinner" style = "margin-top:60px" > < / div >
< / div >
< / 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 >
2026-03-07 15:14:57 +00:00
<!-- ── Accounts submenu popup ──────────────────────────────────────────────── -->
< div class = "accounts-popup" id = "accounts-popup" >
< div class = "accounts-popup-inner" >
< div class = "accounts-popup-header" >
< span > Accounts< / span >
< button class = "icon-btn" onclick = "closeAccountsMenu()" style = "margin:-4px -4px -4px 0" >
< 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 id = "accounts-popup-list" > < / div >
< button class = "accounts-add-btn" onclick = "closeAccountsMenu();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 = "accounts-popup-backdrop" id = "accounts-popup-backdrop" onclick = "closeAccountsMenu()" > < / 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 >
2026-03-07 06:20:39 +00:00
< / div >
2026-03-07 15:14:57 +00:00
< / div >
< div class = "compose-body-wrap" id = "compose-body-wrap" >
2026-03-07 06:20:39 +00:00
< div class = "compose-field" > < label > From< / label > < select id = "compose-from" > < / 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" >
2026-03-07 15:14:57 +00:00
< button class = "btn-secondary" style = "font-size:12px" onclick = "showCCRow()" > +CC< / button >
< button class = "btn-secondary" style = "font-size:12px" onclick = "showBCCRow()" > +BCC< / button >
2026-03-07 06:20:39 +00:00
< 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 >
2026-03-07 15:14:57 +00:00
< 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" >
2026-03-07 16:49:23 +00:00
< p id = "inline-confirm-msg" style = "margin:0 0 14px;font-size:13px;line-height:1.5" > < / p >
2026-03-07 15:14:57 +00:00
< 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 >
2026-03-07 06:20:39 +00:00
< / div >
2026-03-07 15:14:57 +00:00
<!-- ── Add Account Modal ──────────────────────────────────────────────────── -->
2026-03-07 06:20:39 +00:00
< 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 = "18" height = "18" > < 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')" >
< svg viewBox = "0 0 24 24" width = "18" height = "18" fill = "#0078D4" > < path d = "M21.179 4.781H11.25V12h9.929V4.781zM11.25 19.219h9.929V12H11.25v7.219zM2.821 12H11.25V4.781H2.821V12zm0 7.219H11.25V12H2.821v7.219z" / > < / svg >
Outlook
< / button >
< / div >
< div class = "modal-divider" > < span > or add IMAP account< / span > < / div >
2026-03-07 15:20:49 +00:00
< 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 >
2026-03-07 06:20:39 +00:00
< 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 >
2026-03-07 15:20:49 +00:00
< div 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 >
2026-03-07 06:20:39 +00:00
< div class = "modal-row" >
< div class = "modal-field" > < label > IMAP Host< / label > < input type = "text" id = "imap-host" placeholder = "imap.example.com" > < / div >
< div class = "modal-field" > < label > IMAP Port< / label > < input type = "number" id = "imap-port" value = "993" > < / div >
< / div >
< div class = "modal-row" >
< 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 = "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 >
2026-03-07 15:14:57 +00:00
<!-- ── Edit Account Modal ─────────────────────────────────────────────────── -->
2026-03-07 06:20:39 +00:00
< 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 >
< 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 > IMAP Host< / label > < input type = "text" id = "edit-imap-host" > < / div >
< div class = "modal-field" > < label > IMAP Port< / label > < input type = "number" id = "edit-imap-port" > < / div >
< / div >
< div class = "modal-row" >
< 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 class = "settings-group-title" style = "margin:16px 0 8px" > Sync Settings< / div >
< div class = "modal-field" >
< label > Import mode< / 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 = "days" > Last N days< / option >
< option value = "all" > Full mailbox (all email)< / option >
< / select >
< / div >
< div class = "modal-row" id = "edit-sync-days-row" >
< div class = "modal-field" > < label > Days to fetch< / label > < input type = "number" id = "edit-sync-days" value = "30" min = "1" max = "3650" > < / 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 = "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 >
2026-03-07 15:14:57 +00:00
<!-- ── Settings Modal ─────────────────────────────────────────────────────── -->
2026-03-07 06:20:39 +00:00
< div class = "modal-overlay" id = "settings-modal" >
< div class = "modal" style = "width:520px" >
< div style = "display:flex;align-items:center;justify-content:space-between;margin-bottom:22px" >
< 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 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 >
< / div >
<!-- Context menu -->
< div class = "ctx-menu" id = "ctx-menu" > < / div >
< div class = "toast-container" id = "toast-container" > < / div >
{{end}}
{{define "scripts"}}
2026-03-07 16:49:23 +00:00
< script src = "/static/js/app.js?v=7" > < / script >
2026-03-07 15:20:49 +00:00
{{end}}