hermes-site/messenger/index.html
Local Dev 60821e4eae hermes.x: fix the top-nav wordmark
Rendered as "hermes .x" with a visible gap because the brand link's
inline-flex (gap:8px) treated the text and the <span class="x"> as two
separate flex children, so the gap meant for the mark→text separation
landed between the two halves of the name too. Wrap the whole wordmark
in one span and give the display name its capital letters at the same
time, so the brand reads "Hermes.X" like the kit says.

Three places share the markup — the top nav on index, branding and
messenger — all three fixed.
2026-10-21 22:24:59 +03:00

242 lines
12 KiB
HTML

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>hermes.x/messenger — browser messenger (preview)</title>
<meta name="description" content="The web version of Hermes: encrypted DMs over Nostr, addressed by on-chain registered name, running in your browser. Preview of the UI; functional release pending.">
<link rel="icon" href="../assets/favicon.svg" type="image/svg+xml">
<link rel="stylesheet" href="../assets/shared.css">
<style>
/* ---- status banner ---- */
.status-banner{
background:rgba(255,199,95,.08);
border:1px solid rgba(255,199,95,.26);
border-radius:12px;
padding:14px 18px;
margin:1.5rem auto;
max-width:780px;
font-size:14px;
color:var(--ink);
display:flex;gap:14px;align-items:flex-start;
}
.status-banner .dot{
width:10px;height:10px;border-radius:50%;background:var(--warn);
margin-top:6px;flex-shrink:0;animation:pulse 2s ease-in-out infinite;
}
@keyframes pulse { 50% { opacity:.4 } }
.status-banner b{color:var(--warn)}
/* ---- mockup frame ---- */
.mockup{
background:var(--panel);border:1px solid var(--line2);border-radius:16px;
overflow:hidden;margin:1.5rem auto;max-width:860px;
box-shadow:0 20px 60px rgba(0,0,0,.4);
}
.mockup .window-bar{
display:flex;align-items:center;gap:8px;padding:10px 14px;
background:var(--panel2);border-bottom:1px solid var(--line);
}
.mockup .window-bar .dot{width:11px;height:11px;border-radius:50%;background:#444a56}
.mockup .window-bar .dot:nth-child(1){background:#ff5f57}
.mockup .window-bar .dot:nth-child(2){background:#febc2e}
.mockup .window-bar .dot:nth-child(3){background:#28c840}
.mockup .window-bar .title{color:var(--mut);font-size:12px;margin-left:10px}
.mockup .window-bar .status{margin-left:auto;font-size:11px;color:var(--ok);padding:2px 10px;border-radius:999px;background:rgba(79,209,165,.14)}
.mockup .window-bar .npub{color:var(--mut);font-size:11px;font-family:ui-monospace,monospace;margin-left:12px}
.mockup .npub b{color:var(--sky)}
.mockup .app{display:grid;grid-template-columns:220px 1fr;min-height:420px}
.mockup .side{background:var(--panel2);border-right:1px solid var(--line);display:flex;flex-direction:column}
.mockup .side-head{padding:10px;display:flex;gap:6px;border-bottom:1px solid var(--line)}
.mockup .side-head input{flex:1;background:var(--panel);border:1px solid var(--line);border-radius:6px;padding:6px 10px;color:var(--mut);font-size:12px}
.mockup .side-head button{background:var(--sky);color:#06111a;border:none;border-radius:6px;padding:6px 10px;font-weight:600;cursor:pointer;font-size:13px}
.mockup .contact{display:flex;gap:10px;align-items:center;padding:9px 12px;border-bottom:1px solid rgba(255,255,255,.03);cursor:pointer}
.mockup .contact.active{background:var(--panel3)}
.mockup .contact .avatar{width:28px;height:28px;border-radius:50%;background:linear-gradient(135deg,#2a3a4a,#1a2432);display:flex;align-items:center;justify-content:center;font-size:11px;color:var(--mut);font-weight:600;text-transform:uppercase}
.mockup .contact .meta{flex:1;min-width:0}
.mockup .contact .name{font-size:12.5px;color:var(--ink);font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.mockup .contact .last{font-size:11px;color:var(--mut);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.mockup .contact .badge{background:var(--sky);color:#06111a;font-size:10px;font-weight:700;padding:1px 7px;border-radius:999px}
.mockup .thread{display:flex;flex-direction:column;background:var(--bg)}
.mockup .thread-head{padding:12px 16px;border-bottom:1px solid var(--line);display:flex;align-items:center;gap:12px}
.mockup .thread-head .name{font-weight:600;font-size:13px}
.mockup .thread-head .pk{font-family:ui-monospace,monospace;font-size:10.5px;color:var(--mut)}
.mockup .thread-body{flex:1;padding:14px 16px;display:flex;flex-direction:column;gap:6px}
.mockup .bubble{max-width:70%;padding:7px 11px;border-radius:14px;font-size:13px;line-height:1.4}
.mockup .bubble.in{background:#1d2634;align-self:flex-start;border-bottom-left-radius:4px}
.mockup .bubble.out{background:#0d2a3a;align-self:flex-end;border-bottom-right-radius:4px;color:#d9ecfa}
.mockup .bubble .time{display:block;font-size:10px;color:var(--dim);margin-top:3px}
.mockup .compose{display:flex;gap:8px;padding:10px 14px;border-top:1px solid var(--line);background:var(--panel)}
.mockup .compose input{flex:1;background:var(--bg);border:1px solid var(--line);border-radius:8px;padding:9px 11px;color:var(--mut);font-size:13px}
.mockup .compose button{background:var(--sky);color:#06111a;border:none;padding:0 18px;border-radius:8px;font-weight:600;cursor:pointer}
.plan-grid{display:grid;grid-template-columns:1fr 1fr;gap:14px}
@media (max-width:700px){.plan-grid{grid-template-columns:1fr}}
.plan{background:var(--panel);border:1px solid var(--line);border-radius:12px;padding:16px}
.plan h3{display:flex;align-items:center;gap:8px}
.plan ul{margin:.5rem 0 0;padding:0 0 0 1.2rem;color:var(--mut);font-size:14px;line-height:1.7}
.mockup-caption{text-align:center;color:var(--dim);font-size:12px;margin-top:-.5rem;margin-bottom:1.5rem}
</style>
</head>
<body>
<nav class="topnav">
<a class="brand" href="../">
<img src="../assets/mark.webp" alt="Hermes" width="5" height="22" style="display:block">
<span>Hermes<span class="x">.X</span></span>
</a>
<a href="../">Overview</a>
<a href="./" class="here">Messenger</a>
<a href="../branding/">Branding</a>
<a href="https://theseus.x/" class="get">Get Theseus</a>
</nav>
<header class="hero">
<div class="mark" style="color:var(--sky); line-height:0; display:inline-flex" aria-hidden="true">
<img src="../assets/mark.webp" alt="Hermes" width="96" height="96" style="display:block">
</div>
<h1>Messenger</h1>
<p class="tag">Hermes in a browser tab — end-to-end encrypted DMs over Nostr,
addressed by on-chain registered name. The desktop client has
the richest flavour; the web messenger meets you where you are.</p>
</header>
<div class="status-banner">
<div class="dot"></div>
<div>
<b>Preview.</b> The desktop messenger inside <a href="https://theseus.x/">Theseus</a>
is live today — threaded chat, persistent history, address book, keyboard
shortcut <code class="inline">Ctrl+Shift+E</code>. The web-only client
(what this page becomes) is in development: BIP-39 → Nostr key derivation,
BCH-name resolution via gateway, NIP-17 wrap/unwrap, WebSocket to relays,
IndexedDB inbox. Everything client-side, nothing uploaded.
</div>
</div>
<div class="mockup" aria-label="Messenger UI preview">
<div class="window-bar">
<div class="dot"></div><div class="dot"></div><div class="dot"></div>
<div class="title">Hermes · hermes.x/messenger</div>
<span class="npub">your npub: <b>npub1ab…z89</b></span>
<span class="status">1/1 relay</span>
</div>
<div class="app">
<aside class="side">
<div class="side-head"><input value="Search contacts…" readonly><button>+</button></div>
<div class="contact">
<div class="avatar">AL</div>
<div class="meta"><div class="name">alice</div><div class="last">you: see you tomorrow</div></div>
</div>
<div class="contact active">
<div class="avatar">BO</div>
<div class="meta"><div class="name">bob</div><div class="last">great, send me the link</div></div>
<div class="badge">2</div>
</div>
<div class="contact">
<div class="avatar">TE</div>
<div class="meta"><div class="name">test</div><div class="last">chipnet is working</div></div>
</div>
</aside>
<section class="thread">
<div class="thread-head">
<div>
<div class="name">bob</div>
<div class="pk">023bf8c1…9ad2</div>
</div>
</div>
<div class="thread-body">
<div class="bubble in">hey, did you register your name yet?<span class="time">11:38</span></div>
<div class="bubble out">just now — bob.x is live<span class="time">11:40</span></div>
<div class="bubble in">nice. send me the hermes.x link<span class="time">11:42</span></div>
<div class="bubble out">https://hermes.x/messenger<span class="time">11:43</span></div>
<div class="bubble in">great, send me the link<span class="time">11:44</span></div>
</div>
<div class="compose">
<input value="Message…" readonly>
<button>Send</button>
</div>
</section>
</div>
</div>
<div class="mockup-caption">Non-interactive preview — live build pending (see roadmap below).</div>
<div class="wrap narrow">
<section>
<h2>Two clients, one identity</h2>
<div class="plan-grid">
<div class="plan">
<h3>Desktop (Theseus) <span class="src ok">live</span></h3>
<ul>
<li>Full crypto in-process (NIP-17, nip44 v2)</li>
<li>Persistent inbox + read-state on disk</li>
<li>Address book (local JSON + vCard import)</li>
<li>Sidebar panel, Ctrl+Shift+E shortcut</li>
<li>Vault-bind: no re-typing the mnemonic</li>
<li>Reverse-resolve pk → on-chain name</li>
</ul>
<div class="cta" style="justify-content:flex-start;margin-top:1.2rem">
<a class="btn primary" href="https://theseus.x/">Get Theseus</a>
</div>
</div>
<div class="plan">
<h3>Web (this page) <span class="src next">building</span></h3>
<ul>
<li>Same crypto, same identity (same wallet seed derivation)</li>
<li>BCH name resolution via public gateway</li>
<li>WebSocket to Nostr relays from the browser</li>
<li>IndexedDB inbox — survives tab close, scoped to this origin</li>
<li>No download, no install, no account</li>
<li>Mnemonic entry only — never stored in plaintext</li>
</ul>
<div class="cta" style="justify-content:flex-start;margin-top:1.2rem">
<a class="btn ghost" href="../">Why Hermes</a>
</div>
</div>
</div>
</section>
<section>
<h2>Why wait for the web client</h2>
<p class="lede">The web messenger isn't ready yet because of three things we
want to get right before shipping:</p>
<ol class="steps">
<li><b>Key handling in a tab.</b>
<span>A wallet mnemonic is the most sensitive bit of data a user has. In
the desktop app it lives in main-process memory; in a tab it has to
survive page reloads without ever landing in plaintext anywhere. We're
wiring a WebCrypto-backed non-extractable key path with the vault PIN
as the gate.</span></li>
<li><b>Resolver gateway.</b>
<span>The desktop talks to a chipnet Fulcrum directly. A browser can't
— it needs an HTTPS gateway that answers <code class="inline">np</code>
/ <code class="inline">nr</code> for a name. The gateway is live
(<code class="inline">navigate.st/api/dns/alice.x</code>) but the
client-side cache + signature-verification path still has an edge case
with lapsed names.</span></li>
<li><b>Relay persistence.</b>
<span>Desktop keeps a long-lived WebSocket to each relay. A tab doesn't,
because browsers throttle background sockets. The web client needs
offline queueing and a well-defined "catch-up" fetch on return —
otherwise you re-open the tab and the inbox feels empty.</span></li>
</ol>
<p>Until the three are solid, <b>install Theseus</b> for the real thing.
The web client will land here when the above are green.</p>
<div class="cta">
<a class="btn primary" href="https://theseus.x/">Get Theseus</a>
<a class="btn ghost" href="../branding/">Brand kit</a>
</div>
</section>
</div>
<footer>
<div>hermes.x — a <a href="https://silentmode.st/">Silent Mode</a> project.</div>
<div class="fine">Preview only. The functional web client is being built.</div>
</footer>
</body>
</html>