- NIP-46 remote signer in the page (bunker:// and nostrconnect:// pairing) - Chrome extension (NIP-07) with per-site permission prompts, unlocked by the site via externally_connectable; downloadable from the page - Passkey (WebAuthn PRF) encryption of the Drive key file - NIP-49 backups: download or save to Drive; reveal nsec - Fix: oldest key file wins when two devices sign up at once - Fix: never overwrite an existing kind-0 because a relay was slow - Fix: EOSE tracking; relays reconnect with backoff; verify event signatures - One-click re-sign-in (consent only the first time) - Vendored, pinned nostr-tools; CSP; no third-party scripts but Google's Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
31 lines
1.2 KiB
JavaScript
31 lines
1.2 KiB
JavaScript
const $ = (id) => document.getElementById(id);
|
|
const send = (msg) => chrome.runtime.sendMessage(msg);
|
|
|
|
async function render() {
|
|
const s = await send({ type: "popup-state" });
|
|
$("locked").classList.toggle("hidden", !!s.npub);
|
|
$("unlocked").classList.toggle("hidden", !s.npub);
|
|
if (!s.npub) return;
|
|
$("name").textContent = s.profile?.name || "Your nostr identity";
|
|
$("npub").textContent = s.npub;
|
|
const sites = Object.keys(s.perms);
|
|
$("no-sites").classList.toggle("hidden", sites.length > 0);
|
|
$("sites").replaceChildren(...sites.map((origin) => {
|
|
const li = document.createElement("li");
|
|
const span = document.createElement("span");
|
|
span.className = "mono";
|
|
span.textContent = origin;
|
|
const btn = document.createElement("button");
|
|
btn.className = "small";
|
|
btn.textContent = "Revoke";
|
|
btn.onclick = async () => { await send({ type: "revoke", origin }); render(); };
|
|
li.append(span, btn);
|
|
return li;
|
|
}));
|
|
}
|
|
|
|
$("unlock").onclick = async () => { await send({ type: "open-goostr" }); window.close(); };
|
|
$("lock").onclick = async () => { await send({ type: "lock" }); render(); };
|
|
chrome.storage.session.onChanged.addListener(render);
|
|
render();
|