diff --git a/bundled-addons/hermes/panel.html b/bundled-addons/hermes/panel.html index 3acdb1b1..cc7d9fc1 100644 --- a/bundled-addons/hermes/panel.html +++ b/bundled-addons/hermes/panel.html @@ -144,7 +144,8 @@ display: none; place-items: center; z-index: 100; padding: 12px; } .modal-bg.show { display: grid; } .modal { background: var(--panel); border: 1px solid var(--line); - border-radius: 12px; padding: 16px; width: 100%; max-width: 320px; } + border-radius: 12px; padding: 16px; width: 100%; max-width: 320px; + max-height: calc(100vh - 24px); overflow-y: auto; -webkit-overflow-scrolling: touch; } .modal h3 { margin: 0 0 10px; font-size: 14px; } .modal input { font: inherit; font-size: 12.5px; width: 100%; padding: 8px 10px; background: var(--panel-2); color: var(--ink); @@ -216,6 +217,15 @@ We hash the number locally and publish a Nostr claim for the hash. Anyone who already knows your number can find you — same tradeoff as number-based messengers. Unlink any time. +
Address book
+ + +
+
+
+ Open a .vcf export from your contacts. We parse it locally, look up any linked phone numbers against Hermes, and show the matches to add — nothing uploaded. +
+
Backup
No seed phrase stored for this account.
(created-elsewhere / vault / mnemonic signins don't retain the words)
@@ -774,6 +784,48 @@ $("openSettingsBtn").addEventListener("click", async () => { await openSettings(); }); $("settingsClose").addEventListener("click", () => $("settingsModal").classList.remove("show")); +// --- vCard import (Address book) --- +$("importVcardBtn").addEventListener("click", () => $("vcardFileInput").click()); +$("vcardFileInput").addEventListener("change", async () => { + const f = $("vcardFileInput").files && $("vcardFileInput").files[0]; + if (!f) return; + err($("vcardErr"), ""); + $("vcardResult").textContent = "Reading " + f.name + "\u2026"; + try { + const text = await f.text(); + // Minimal RFC 6350 subset: pull FN + TEL per VCARD block. + const entries = []; + for (const block of text.split(/BEGIN:VCARD/i).slice(1)) { + const body = block.split(/END:VCARD/i)[0] || ""; + const fn = (body.match(/^FN(?:;[^:]*)?:(.+)$/mi) || [])[1]; + const tel = (body.match(/^TEL(?:;[^:]*)?:(.+)$/mi) || [])[1]; + if (!tel) continue; + entries.push({ name: (fn || "").trim(), phone: tel.trim() }); + } + if (!entries.length) { $("vcardResult").textContent = "No phone numbers in that file."; return; } + $("vcardResult").textContent = "Looking up " + entries.length + " number(s) on Hermes\u2026"; + const r = await bridge.invoke("findByPhones", { phones: entries.map(e => e.phone) }); + if (!r || !r.ok) throw new Error((r && r.err) || "lookup failed"); + const found = r.matches || []; + if (!found.length) { $("vcardResult").textContent = "No matches among your contacts yet."; return; } + let added = 0; + for (const m of found) { + const label = (entries.find(e => e.phone.replace(/\D+/g, "") === m.phone.replace(/\D+/g, "")) || {}).name || m.phone; + try { + const a = await bridge.invoke("contactsAdd", { name: m.pkHex, nickname: label }); + if (a && a.ok) added++; + } catch {} + } + $("vcardResult").textContent = "Added " + added + " of " + found.length + " matches. Refresh the chat list."; + try { const r2 = await bridge.invoke("contactsList"); if (r2 && r2.ok) { state.contacts = r2.contacts || []; renderContacts(); } } catch {} + } catch (e) { + err($("vcardErr"), (e && e.message) || String(e)); + $("vcardResult").textContent = ""; + } finally { + $("vcardFileInput").value = ""; + } +}); + $("settingsModal").addEventListener("click", (e) => { if (e.target === $("settingsModal")) $("settingsModal").classList.remove("show"); }); // Display-mode picker. Persisted per-profile via silentmode.storage (the