Shortcut: Ctrl+Shift+M -> Ctrl+Shift+E. Chrome owns Ctrl+Shift+M as
'switch profile' and E matches the Greek spelling (Ermis).
Sidebar discoverability: new bundled add-on at bundled-addons/hermes/
registers a left-strip icon + launcher panel. The panel is a thin chrome
(mark + 'Open Messages' + shortcut hint) that forwards clicks through
api.onMessage -> ipcMain.emit('hermes-open-request') -> openHermesWindow.
All real messaging code stays in main.js — add-on is purely for discovery.
Address book: opt-in vCard (.vcf) import. Dialog in the Add Contact modal
lets the user pick a file; main.js parses BEGIN:VCARD blocks for X-BNS-NAME
/ X-NOSTR-NPUB / X-NOSTR-PUBKEY plus .bch names and npubs mentioned in
NOTE fields. Candidates are previewed per-contact with checkboxes; nothing
is added until the user confirms. Non-matching vCards are returned too (so
the user can still add manually) rather than silently dropped. Nothing is
uploaded; parsing is local.
site/hermes: status badge flipped from 'designed - not yet built' to
'MVP - desktop'. New 'What ships today' grid covers threaded chat, send by
name, address book, local history (all live) and mobile + Tor (next /
planned). Hero carries the new Ctrl+Shift+E shortcut and a link to
/hermes/branding.
site/hermes/branding: new page. Mark SVG (winged envelope, two variants
for 16-20px and 24-160px display sizes), palette (reuses Silent Mode
tokens + an amber accent that's distinctly warm-yellow, not Hermes-orange
— so it never collides with Hermès the fashion house), name explanation
(English Hermes / Greek Ermis), voice notes, do/don't table, MPL-2.0
attribution.
Hermes/MOBILE-RECOMMENDATION.md: analysis of three mobile paths (inside
Ariadne / standalone / hybrid). Recommendation is Option 1: ship as a
screen inside Ariadne. Reuses the wallet seed, aligns with the design
doc's 'one seed many purposes' architecture, lowest total cost.
43 lines
2 KiB
JavaScript
43 lines
2 KiB
JavaScript
// Renderer bridge for the Messages panel.
|
|
// All hermes-* IPC returns { ok, ... } | { ok: false, err }.
|
|
const { contextBridge, ipcRenderer } = require("electron");
|
|
|
|
contextBridge.exposeInMainWorld("hermes", {
|
|
// Identity + relay state
|
|
status: () => ipcRenderer.invoke("hermes-status"),
|
|
canUseVault: () => ipcRenderer.invoke("hermes-can-use-vault"),
|
|
init: (mnemonic) => ipcRenderer.invoke("hermes-init", { mnemonic }),
|
|
initFromVault: () => ipcRenderer.invoke("hermes-init", { useVault: true }),
|
|
close: () => ipcRenderer.invoke("hermes-close"),
|
|
|
|
// Messages
|
|
inbox: () => ipcRenderer.invoke("hermes-inbox"),
|
|
send: (to, text) => ipcRenderer.invoke("hermes-send", { to, text }),
|
|
markThreadRead: (peerPkHex, upToCreatedAt) =>
|
|
ipcRenderer.invoke("hermes-thread-mark-read", { peerPkHex, upToCreatedAt }),
|
|
|
|
// Contacts (address book)
|
|
contacts: {
|
|
list: () => ipcRenderer.invoke("hermes-contacts-list"),
|
|
add: (name, nickname) => ipcRenderer.invoke("hermes-contacts-add", { name, nickname }),
|
|
update: (pkHex, patch) => ipcRenderer.invoke("hermes-contacts-update", { pkHex, patch }),
|
|
remove: (pkHex) => ipcRenderer.invoke("hermes-contacts-remove", { pkHex }),
|
|
// Opt-in import: opens an OS file-picker for a .vcf. Returns parsed
|
|
// candidates for the renderer to preview; nothing is added without a
|
|
// follow-up add() call per contact. Convenience only — the full add
|
|
// flow (manual +) is never gated behind this.
|
|
importPick: () => ipcRenderer.invoke("hermes-contacts-import-pick"),
|
|
},
|
|
|
|
// Pushes: new decrypted message / relay status change.
|
|
onMessage: (cb) => {
|
|
const wrapped = (_e, msg) => cb(msg);
|
|
ipcRenderer.on("hermes-message", wrapped);
|
|
return () => ipcRenderer.removeListener("hermes-message", wrapped);
|
|
},
|
|
onStatus: (cb) => {
|
|
const wrapped = (_e, s) => cb(s);
|
|
ipcRenderer.on("hermes-status-update", wrapped);
|
|
return () => ipcRenderer.removeListener("hermes-status-update", wrapped);
|
|
},
|
|
});
|