198 lines
12 KiB
HTML
198 lines
12 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>GoMail</title>
|
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
<link href="https://fonts.googleapis.com/css2?family=Source+Serif+4:opsz,wght@8..60,400;8..60,600;8..60,700&display=swap" rel="stylesheet">
|
|
<script src="https://cdn.tailwindcss.com"></script>
|
|
<style>
|
|
:root{
|
|
--bg:#12141c;
|
|
--surface:#1a1d29;
|
|
--surface-2:#20232f;
|
|
--border:#2a2e3d;
|
|
--text:#e8e6e1;
|
|
--text-muted:#8b8d98;
|
|
--text-faint:#5b5e6b;
|
|
--accent:#d9a441;
|
|
--accent-dim:#d9a44122;
|
|
--accent-text:#f0c878;
|
|
--danger:#e2685a;
|
|
}
|
|
*{box-sizing:border-box}
|
|
body{background:var(--bg);color:var(--text);font-family:system-ui,-apple-system,sans-serif;margin:0;font-size:14px}
|
|
.serif{font-family:'Source Serif 4',Georgia,serif}
|
|
::selection{background:var(--accent-dim)}
|
|
::-webkit-scrollbar{width:10px;height:10px}
|
|
::-webkit-scrollbar-thumb{background:var(--border);border-radius:6px}
|
|
::-webkit-scrollbar-track{background:transparent}
|
|
a{color:inherit}
|
|
|
|
/* ── layout shell ────────────────────────────────────────────── */
|
|
.app-shell{display:flex;height:100vh;overflow:hidden}
|
|
.rail{width:250px;flex:none;background:var(--surface);border-right:1px solid var(--border);display:flex;flex-direction:column}
|
|
.rail-header{padding:18px 16px 10px;display:flex;align-items:center;gap:8px}
|
|
.rail-brand{font-weight:700;font-size:16px;letter-spacing:.01em}
|
|
.rail-body{flex:1;overflow-y:auto;padding:4px 8px}
|
|
.rail-footer{padding:10px 12px;border-top:1px solid var(--border);display:flex;align-items:center;justify-content:space-between}
|
|
.workspace{flex:1;display:flex;min-width:0}
|
|
|
|
/* ── nav rows (accounts + folders) ──────────────────────────────── */
|
|
.section-label{font-size:10px;text-transform:uppercase;letter-spacing:.08em;color:var(--text-faint);padding:14px 10px 6px}
|
|
.nav-row{display:flex;align-items:center;gap:10px;padding:8px 10px;border-radius:8px;cursor:pointer;color:var(--text-muted);font-size:13px}
|
|
.nav-row:hover{background:var(--surface-2)}
|
|
.nav-row.active{background:var(--accent-dim);color:var(--accent-text)}
|
|
.seal{width:22px;height:22px;border-radius:50%;flex:none;display:flex;align-items:center;justify-content:center;font-size:10px;font-weight:700;background:var(--surface-2);color:var(--text-muted);border:1px solid var(--border)}
|
|
.nav-row.active .seal{background:var(--accent);color:#1a1206;border-color:var(--accent)}
|
|
.nav-row-label{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
.count-badge{font-size:11px;color:var(--text-faint);min-width:16px;text-align:right}
|
|
.nav-row.active .count-badge{color:var(--accent-text)}
|
|
|
|
/* ── message list pane ──────────────────────────────────────────── */
|
|
.list-pane{width:360px;flex:none;border-right:1px solid var(--border);display:flex;flex-direction:column;min-width:0}
|
|
.list-toolbar{padding:12px;border-bottom:1px solid var(--border)}
|
|
.list-title{font-weight:700;font-size:15px;margin-bottom:8px}
|
|
.msg-list{flex:1;overflow-y:auto}
|
|
.msg-row{padding:12px 14px;border-bottom:1px solid var(--border);cursor:pointer}
|
|
.msg-row:hover{background:var(--surface)}
|
|
.msg-row.selected{background:var(--accent-dim)}
|
|
.msg-row .msg-from{display:flex;align-items:center;gap:6px;color:var(--text);font-size:13px}
|
|
.msg-row.unread .msg-from{font-weight:700}
|
|
.msg-row .msg-subject{color:var(--text-muted);font-size:13px;margin-top:2px;font-family:'Source Serif 4',Georgia,serif}
|
|
.msg-row .msg-meta{color:var(--text-faint);font-size:11px;margin-top:4px;display:flex;gap:6px;align-items:center}
|
|
.dot{width:7px;height:7px;border-radius:50%;background:var(--accent);flex:none}
|
|
.chip{font-size:10px;padding:1px 7px;border-radius:9px;background:var(--surface-2);color:var(--text-muted);border:1px solid var(--border)}
|
|
|
|
/* ── reading pane ────────────────────────────────────────────────── */
|
|
.reading-pane{flex:1;overflow-y:auto;padding:28px 32px;min-width:0}
|
|
.reading-empty{color:var(--text-faint);text-align:center;margin-top:80px}
|
|
.reading-subject{font-family:'Source Serif 4',Georgia,serif;font-weight:700;font-size:21px;color:#fff}
|
|
.reading-meta{color:var(--text-muted);font-size:13px;margin-top:6px;line-height:1.6}
|
|
.reading-body{white-space:pre-wrap;font-family:inherit;color:#d4d2ce;font-size:13.5px;line-height:1.6;margin-top:20px}
|
|
|
|
/* ── generic controls ────────────────────────────────────────────── */
|
|
.btn{padding:8px 15px;border-radius:8px;font-size:13px;font-weight:600;cursor:pointer;border:none;transition:filter .12s}
|
|
.btn:hover{filter:brightness(1.1)}
|
|
.btn-primary{background:var(--accent);color:#1a1206}
|
|
.btn-ghost{background:transparent;color:var(--text-muted);border:1px solid var(--border)}
|
|
.btn-danger{background:transparent;color:var(--danger);border:1px solid #e2685a44}
|
|
.btn:focus-visible,.inp:focus-visible,input:focus-visible,select:focus-visible{outline:2px solid var(--accent);outline-offset:1px}
|
|
.inp{background:var(--bg);border:1px solid var(--border);border-radius:8px;padding:9px 12px;color:var(--text);font-size:13px;width:100%}
|
|
.inp::placeholder{color:var(--text-faint)}
|
|
.card{background:var(--surface);border:1px solid var(--border);border-radius:12px;padding:18px}
|
|
.view-single{flex:1;overflow-y:auto;padding:28px 32px}
|
|
.modal-bg{position:fixed;inset:0;background:#00000088;z-index:50;display:flex;align-items:center;justify-content:center}
|
|
.modal{background:var(--surface);border:1px solid var(--border);border-radius:14px;padding:24px;width:560px;max-width:95vw;max-height:88vh;overflow-y:auto}
|
|
.label{font-size:12px;color:var(--text-muted);margin-bottom:4px;display:block}
|
|
.field{margin-bottom:12px}
|
|
.notice{background:var(--accent-dim);border:1px solid var(--accent);color:var(--accent-text);border-radius:8px;padding:10px 12px;font-size:12px;margin-bottom:12px}
|
|
.settings-section{margin-bottom:22px}
|
|
.settings-section h3{font-family:'Source Serif 4',Georgia,serif;font-size:17px;margin:0 0 4px}
|
|
.settings-section p.hint{color:var(--text-muted);font-size:12px;margin:0 0 12px}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<div id="login" style="display:none;min-height:100vh;align-items:center;justify-content:center" class="flex">
|
|
<div class="card" style="width:320px">
|
|
<div style="text-align:center;margin-bottom:20px">
|
|
<div class="seal" style="width:44px;height:44px;font-size:18px;margin:0 auto 10px;background:var(--accent);color:#1a1206;border:none">G</div>
|
|
<h1 class="serif" style="font-weight:700;color:#fff;font-size:22px;margin:0">GoMail</h1>
|
|
</div>
|
|
<div class="field"><input id="le" class="inp" placeholder="you@example.com"></div>
|
|
<div class="field"><input id="lp" type="password" class="inp" placeholder="Password" onkeydown="if(event.key==='Enter')login()"></div>
|
|
<button onclick="login()" class="btn btn-primary" style="width:100%">Sign in</button>
|
|
<p id="lerr" style="display:none;color:var(--danger);font-size:12px;text-align:center;margin-top:10px"></p>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="mfa-login" style="display:none;min-height:100vh;align-items:center;justify-content:center" class="flex">
|
|
<div class="card" style="width:320px">
|
|
<h1 class="serif" style="font-weight:700;color:#fff;font-size:18px;margin:0 0 4px">Two-factor code</h1>
|
|
<p class="hint" style="color:var(--text-muted);font-size:12px;margin:0 0 14px">Enter a code from your authenticator app, or a backup code.</p>
|
|
<div class="field"><input id="mfa-code" class="inp" placeholder="123456" onkeydown="if(event.key==='Enter')mfaVerifyLogin()"></div>
|
|
<button onclick="mfaVerifyLogin()" class="btn btn-primary" style="width:100%">Verify</button>
|
|
<button onclick="usePasskeyLogin()" class="btn btn-ghost" style="width:100%;margin-top:8px">Use a passkey instead</button>
|
|
<p id="mfaerr" style="display:none;color:var(--danger);font-size:12px;text-align:center;margin-top:10px"></p>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="app" style="display:none" class="app-shell">
|
|
<aside class="rail">
|
|
<div class="rail-header">
|
|
<div class="seal" style="background:var(--accent);color:#1a1206;border:none">G</div>
|
|
<div class="rail-brand serif">GoMail</div>
|
|
</div>
|
|
<div style="padding:0 12px 8px">
|
|
<button onclick="openCompose()" class="btn btn-primary" style="width:100%">✎ Compose</button>
|
|
</div>
|
|
<div class="rail-body">
|
|
<div class="section-label">Accounts</div>
|
|
<div id="account-switcher"></div>
|
|
<div id="folder-section">
|
|
<div class="section-label">Folders</div>
|
|
<div id="folder-list"></div>
|
|
</div>
|
|
<div class="section-label"> </div>
|
|
<div class="nav-row" onclick="showQuarantine()" id="nav-quarantine">
|
|
<div class="seal">🔒</div><div class="nav-row-label">Quarantine</div>
|
|
</div>
|
|
<div class="nav-row" onclick="showSettings()" id="nav-settings">
|
|
<div class="seal">⚙</div><div class="nav-row-label">Settings</div>
|
|
</div>
|
|
</div>
|
|
<div class="rail-footer">
|
|
<span id="me-email" style="font-size:12px;color:var(--text-faint);overflow:hidden;text-overflow:ellipsis;white-space:nowrap"></span>
|
|
<button onclick="logout()" style="font-size:11px;color:var(--text-faint);background:none;border:none;cursor:pointer;flex:none">Sign out</button>
|
|
</div>
|
|
</aside>
|
|
|
|
<main class="workspace">
|
|
<div id="view-mail" style="display:flex;flex:1;min-width:0">
|
|
<section class="list-pane">
|
|
<div class="list-toolbar">
|
|
<div class="list-title" id="list-title">Inbox</div>
|
|
<input id="search-box" class="inp" placeholder="Search this account…" oninput="onSearchInput(this.value)">
|
|
<div id="search-body-toggle" style="display:none;margin-top:6px">
|
|
<a href="#" onclick="event.preventDefault();runSearch(true)" style="font-size:11px;color:var(--text-muted)">Search message bodies too (slower)</a>
|
|
</div>
|
|
</div>
|
|
<div class="msg-list" id="msg-list"></div>
|
|
</section>
|
|
<section class="reading-pane" id="msg-view"><div class="reading-empty">Select a message</div></section>
|
|
</div>
|
|
<div id="view-quarantine" class="view-single" style="display:none">
|
|
<h2 class="serif" style="font-weight:700;margin-bottom:16px">Quarantine</h2>
|
|
<div id="quarantine-list"></div>
|
|
</div>
|
|
<div id="view-settings" class="view-single" style="display:none">
|
|
<h2 class="serif" style="font-weight:700;margin-bottom:20px">Settings</h2>
|
|
<div id="settings-body"></div>
|
|
</div>
|
|
</main>
|
|
</div>
|
|
|
|
<div id="compose-modal" class="modal-bg" style="display:none">
|
|
<div class="modal">
|
|
<h3 class="serif" style="font-weight:700;margin-bottom:16px">New message</h3>
|
|
<div class="field">
|
|
<label class="label">From</label>
|
|
<select id="c-from" class="inp"></select>
|
|
</div>
|
|
<div class="field"><input id="c-to" class="inp" placeholder="To"></div>
|
|
<div class="field"><input id="c-subject" class="inp" placeholder="Subject"></div>
|
|
<div class="field"><textarea id="c-body" class="inp" rows="8" placeholder="Write something…"></textarea></div>
|
|
<div style="display:flex;gap:8px;justify-content:flex-end">
|
|
<button onclick="closeCompose()" class="btn btn-ghost">Cancel</button>
|
|
<button onclick="sendMessage()" class="btn btn-primary">Send</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script src="app.js"></script>
|
|
</body>
|
|
</html>
|