Compare commits

...
5 Commits
9 changed files with 121 additions and 125 deletions
+49 -50
View File
@@ -8,7 +8,7 @@
* Two-file architecture:
* 1. Load nostr.bundle.js (official nostr-tools bundle)
* 2. Load nostr-lite.js (this file - NOSTR_LOGIN_LITE library with CSS-only themes)
* Generated on: 2026-05-27T19:17:36.261Z
* Generated on: 2026-07-21T23:30:02.044Z
*/
// Verify dependencies are loaded
@@ -436,7 +436,7 @@ class Modal {
modalContent.appendChild(modalHeader);
// Add version element in bottom-right corner aligned with modal body
const versionElement = document.createElement('div');
versionElement.textContent = 'v0.1.19';
versionElement.textContent = 'v0.1.22';
versionElement.style.cssText = `
position: absolute;
bottom: 8px;
@@ -1663,7 +1663,7 @@ class Modal {
try {
// Incremental step 1:
// chooser + capture + driver open only (no getPublicKey/auth yet).
const selectedDevice = await navigator.usb.requestDevice({ filters: [{ vendorId: 0x303a }] });
const selectedDevice = await navigator.usb.requestDevice({ filters: [{}] });
this._nsignerSelectedDevice = selectedDevice;
if (!window.NSignerWebUSB || typeof window.NSignerWebUSB !== 'function') {
@@ -2008,10 +2008,7 @@ class Modal {
if (!driver && canUsb) {
try {
const pairedUsb = await window.NSignerWebUSB.getPairedDevice({
vendorId: 0x303a,
productId: 0x4001
});
const pairedUsb = await window.NSignerWebUSB.getPairedDevice();
if (pairedUsb) {
renderConnectingScreen('Reconnecting to paired WebUSB device...');
transport = 'webusb';
@@ -2055,7 +2052,7 @@ class Modal {
} else if (target === 'webusb') {
renderConnectingScreen('Waiting for WebUSB device selection...');
try {
const selectedDevice = await navigator.usb.requestDevice({ filters: [{ vendorId: 0x303a }] });
const selectedDevice = await navigator.usb.requestDevice({ filters: [{}] });
transport = 'webusb';
driver = await connectUsbFromDevice(selectedDevice);
} catch (usbErr) {
@@ -2300,7 +2297,7 @@ class Modal {
throw new Error('NSignerWebUSB driver missing');
}
const selectedDevice = await navigator.usb.requestDevice({ filters: [{ vendorId: 0x303a }] });
const selectedDevice = await navigator.usb.requestDevice({ filters: [{}] });
const driver = new window.NSignerWebUSB(selectedDevice, {
callerSecretKey: caller.secret,
@@ -3364,9 +3361,9 @@ window.addEventListener('load', () => {
// ======================================
class NSignerWebUSB {
// Keep picker broad (vendor-level) so Chromium can show all matching Feather states.
// Product-level checks still happen during open()/RPC.
static FILTERS = [{ vendorId: 0x303a }];
// Keep WebUSB chooser unfiltered so users can select any USB device.
// WebUSB requires a non-empty filters array; {} matches all devices.
static FILTERS = [{}];
static async requestAndConnect(options = {}) {
if (!('usb' in navigator)) {
@@ -3385,13 +3382,14 @@ class NSignerWebUSB {
const devices = await navigator.usb.getDevices();
if (!devices || devices.length === 0) return null;
const vendorId = options.vendorId ?? 0x303a;
const vendorId = options.vendorId ?? null;
const productId = options.productId ?? null;
const serial = options.serialNumber || null;
return devices.find((d) => {
const vendorMatch = d.vendorId === vendorId;
if (!vendorMatch) return false;
if (vendorId !== null && vendorId !== undefined && d.vendorId !== vendorId) {
return false;
}
if (productId !== null && productId !== undefined && d.productId !== productId) {
return false;
@@ -3518,43 +3516,43 @@ class NSignerWebUSB {
async getPublicKey() {
const params = [{ nostr_index: this.nostrIndex }];
const resp = await this._rpcCall('get_public_key', params);
const resp = await this._rpcCall('nostr_get_public_key', params);
if (!resp || typeof resp.result !== 'string') {
throw new Error('Invalid get_public_key response');
throw new Error('Invalid nostr_get_public_key response');
}
return resp.result.trim().toLowerCase();
}
async signEvent(unsignedEvent) {
const params = [unsignedEvent, { nostr_index: this.nostrIndex }];
const resp = await this._rpcCall('sign_event', params);
const resp = await this._rpcCall('nostr_sign_event', params);
if (!resp || typeof resp.result !== 'object') {
throw new Error('Invalid sign_event response');
throw new Error('Invalid nostr_sign_event response');
}
return resp.result;
}
async nip04Encrypt(peerHex, plaintext) {
const resp = await this._rpcCall('nip04_encrypt', [peerHex, plaintext, { nostr_index: this.nostrIndex }]);
if (!resp || typeof resp.result !== 'string') throw new Error('Invalid nip04_encrypt response');
const resp = await this._rpcCall('nostr_nip04_encrypt', [peerHex, plaintext, { nostr_index: this.nostrIndex }]);
if (!resp || typeof resp.result !== 'string') throw new Error('Invalid nostr_nip04_encrypt response');
return resp.result;
}
async nip04Decrypt(peerHex, ciphertext) {
const resp = await this._rpcCall('nip04_decrypt', [peerHex, ciphertext, { nostr_index: this.nostrIndex }]);
if (!resp || typeof resp.result !== 'string') throw new Error('Invalid nip04_decrypt response');
const resp = await this._rpcCall('nostr_nip04_decrypt', [peerHex, ciphertext, { nostr_index: this.nostrIndex }]);
if (!resp || typeof resp.result !== 'string') throw new Error('Invalid nostr_nip04_decrypt response');
return resp.result;
}
async nip44Encrypt(peerHex, plaintext) {
const resp = await this._rpcCall('nip44_encrypt', [peerHex, plaintext, { nostr_index: this.nostrIndex }]);
if (!resp || typeof resp.result !== 'string') throw new Error('Invalid nip44_encrypt response');
const resp = await this._rpcCall('nostr_nip44_encrypt', [peerHex, plaintext, { nostr_index: this.nostrIndex }]);
if (!resp || typeof resp.result !== 'string') throw new Error('Invalid nostr_nip44_encrypt response');
return resp.result;
}
async nip44Decrypt(peerHex, ciphertext) {
const resp = await this._rpcCall('nip44_decrypt', [peerHex, ciphertext, { nostr_index: this.nostrIndex }]);
if (!resp || typeof resp.result !== 'string') throw new Error('Invalid nip44_decrypt response');
const resp = await this._rpcCall('nostr_nip44_decrypt', [peerHex, ciphertext, { nostr_index: this.nostrIndex }]);
if (!resp || typeof resp.result !== 'string') throw new Error('Invalid nostr_nip44_decrypt response');
return resp.result;
}
@@ -3727,19 +3725,17 @@ if (typeof window !== 'undefined') {
// ======================================
class NSignerWebSerial {
// CH340 VID:PID 0x1a86:0x7523 (most CYD revisions)
// CP2102 VID:PID 0x10c4:0xea60 (some CYD revisions)
static FILTERS = [
{ usbVendorId: 0x1a86, usbProductId: 0x7523 },
{ usbVendorId: 0x10c4, usbProductId: 0xea60 }
];
// Keep Web Serial chooser unfiltered so users can select any serial port.
static FILTERS = [];
static async requestAndConnect(options = {}) {
if (!('serial' in navigator)) {
throw new Error('Web Serial API not available in this browser (requires Chrome 89+, Edge 89+, or Brave)');
}
const port = await navigator.serial.requestPort({ filters: NSignerWebSerial.FILTERS });
const port = NSignerWebSerial.FILTERS.length
? await navigator.serial.requestPort({ filters: NSignerWebSerial.FILTERS })
: await navigator.serial.requestPort();
const driver = new NSignerWebSerial(port, options);
await driver.open();
return driver;
@@ -3918,43 +3914,43 @@ class NSignerWebSerial {
async getPublicKey() {
const params = [{ nostr_index: this.nostrIndex }];
const resp = await this._rpcCall('get_public_key', params);
const resp = await this._rpcCall('nostr_get_public_key', params);
if (!resp || typeof resp.result !== 'string') {
throw new Error('Invalid get_public_key response');
throw new Error('Invalid nostr_get_public_key response');
}
return resp.result.trim().toLowerCase();
}
async signEvent(unsignedEvent) {
const params = [unsignedEvent, { nostr_index: this.nostrIndex }];
const resp = await this._rpcCall('sign_event', params);
const resp = await this._rpcCall('nostr_sign_event', params);
if (!resp || typeof resp.result !== 'object') {
throw new Error('Invalid sign_event response');
throw new Error('Invalid nostr_sign_event response');
}
return resp.result;
}
async nip04Encrypt(peerHex, plaintext) {
const resp = await this._rpcCall('nip04_encrypt', [peerHex, plaintext, { nostr_index: this.nostrIndex }]);
if (!resp || typeof resp.result !== 'string') throw new Error('Invalid nip04_encrypt response');
const resp = await this._rpcCall('nostr_nip04_encrypt', [peerHex, plaintext, { nostr_index: this.nostrIndex }]);
if (!resp || typeof resp.result !== 'string') throw new Error('Invalid nostr_nip04_encrypt response');
return resp.result;
}
async nip04Decrypt(peerHex, ciphertext) {
const resp = await this._rpcCall('nip04_decrypt', [peerHex, ciphertext, { nostr_index: this.nostrIndex }]);
if (!resp || typeof resp.result !== 'string') throw new Error('Invalid nip04_decrypt response');
const resp = await this._rpcCall('nostr_nip04_decrypt', [peerHex, ciphertext, { nostr_index: this.nostrIndex }]);
if (!resp || typeof resp.result !== 'string') throw new Error('Invalid nostr_nip04_decrypt response');
return resp.result;
}
async nip44Encrypt(peerHex, plaintext) {
const resp = await this._rpcCall('nip44_encrypt', [peerHex, plaintext, { nostr_index: this.nostrIndex }]);
if (!resp || typeof resp.result !== 'string') throw new Error('Invalid nip44_encrypt response');
const resp = await this._rpcCall('nostr_nip44_encrypt', [peerHex, plaintext, { nostr_index: this.nostrIndex }]);
if (!resp || typeof resp.result !== 'string') throw new Error('Invalid nostr_nip44_encrypt response');
return resp.result;
}
async nip44Decrypt(peerHex, ciphertext) {
const resp = await this._rpcCall('nip44_decrypt', [peerHex, ciphertext, { nostr_index: this.nostrIndex }]);
if (!resp || typeof resp.result !== 'string') throw new Error('Invalid nip44_decrypt response');
const resp = await this._rpcCall('nostr_nip44_decrypt', [peerHex, ciphertext, { nostr_index: this.nostrIndex }]);
if (!resp || typeof resp.result !== 'string') throw new Error('Invalid nostr_nip44_decrypt response');
return resp.result;
}
@@ -4170,12 +4166,15 @@ class NSignerWebSerial {
const vid = matchInfo?.usbVendorId;
const pid = matchInfo?.usbProductId;
return ports.find((p) => {
// Prefer exact VID/PID match when available, but do not require it.
const exact = ports.find((p) => {
const i = p.getInfo?.() || {};
if (vid != null && i.usbVendorId !== vid) return false;
if (pid != null && i.usbProductId !== pid) return false;
return true;
}) || ports[0] || null;
});
return exact || ports[0] || null;
} catch (_) {
return null;
}
@@ -5958,8 +5957,8 @@ class AuthManager {
}
const paired = await window.NSignerWebUSB.getPairedDevice({
vendorId: nsigner.deviceVid ?? 0x303a,
productId: nsigner.deviceProductId ?? 0x4001,
vendorId: nsigner.deviceVid ?? null,
productId: nsigner.deviceProductId ?? null,
serialNumber: nsigner.deviceSerial || undefined
});
+6 -6
View File
@@ -11146,12 +11146,12 @@ zoo`.split("\n");
}
async getPublicKey() {
if (!this.cachedPubKey) {
this.cachedPubKey = await this.sendRequest("get_public_key", []);
this.cachedPubKey = await this.sendRequest("nostr_get_public_key", []);
}
return this.cachedPubKey;
}
async signEvent(event) {
let resp = await this.sendRequest("sign_event", [JSON.stringify(event)]);
let resp = await this.sendRequest("nostr_sign_event", [JSON.stringify(event)]);
let signed = JSON.parse(resp);
if (verifyEvent(signed)) {
return signed;
@@ -11160,16 +11160,16 @@ zoo`.split("\n");
}
}
async nip04Encrypt(thirdPartyPubkey, plaintext) {
return await this.sendRequest("nip04_encrypt", [thirdPartyPubkey, plaintext]);
return await this.sendRequest("nostr_nip04_encrypt", [thirdPartyPubkey, plaintext]);
}
async nip04Decrypt(thirdPartyPubkey, ciphertext) {
return await this.sendRequest("nip04_decrypt", [thirdPartyPubkey, ciphertext]);
return await this.sendRequest("nostr_nip04_decrypt", [thirdPartyPubkey, ciphertext]);
}
async nip44Encrypt(thirdPartyPubkey, plaintext) {
return await this.sendRequest("nip44_encrypt", [thirdPartyPubkey, plaintext]);
return await this.sendRequest("nostr_nip44_encrypt", [thirdPartyPubkey, plaintext]);
}
async nip44Decrypt(thirdPartyPubkey, ciphertext) {
return await this.sendRequest("nip44_decrypt", [thirdPartyPubkey, ciphertext]);
return await this.sendRequest("nostr_nip44_decrypt", [thirdPartyPubkey, ciphertext]);
}
};
async function createAccount(bunker, params, username, domain, email, localSecretKey = generateSecretKey()) {
+11 -13
View File
@@ -249,12 +249,8 @@
const nip44DecOutEl = document.getElementById("nip44DecOut");
// ── State ─────────────────────────────────────────────────────────────────
// CH340 VID:PID 0x1a86:0x7523 (most CYD revisions)
// CP2102 VID:PID 0x10c4:0xea60 (some CYD revisions)
const SERIAL_FILTERS = [
{ usbVendorId: 0x1a86, usbProductId: 0x7523 },
{ usbVendorId: 0x10c4, usbProductId: 0xea60 }
];
// Keep chooser unfiltered so any serial-capable device can be selected.
const SERIAL_FILTERS = [];
let port = null; // SerialPort
let writer = null; // WritableStreamDefaultWriter
@@ -507,7 +503,9 @@
throw new Error("Web Serial API not available — use Chrome 89+, Edge 89+, or Brave");
}
const initiallySelected = await navigator.serial.requestPort({ filters: SERIAL_FILTERS });
const initiallySelected = SERIAL_FILTERS.length
? await navigator.serial.requestPort({ filters: SERIAL_FILTERS })
: await navigator.serial.requestPort();
const selectedInfo = initiallySelected.getInfo?.() || {};
let lastError = null;
@@ -613,7 +611,7 @@
// ── Pubkey helper ─────────────────────────────────────────────────────────
async function fetchOwnPubkeyAndFillPeers() {
const params = [getIndexOptions()];
const resp = await rpcCall("get_public_key", params);
const resp = await rpcCall("nostr_get_public_key", params);
if (resp && typeof resp.result === "string") {
ownPubkey = resp.result.trim().toLowerCase();
pubkeyOutEl.textContent = pretty(resp);
@@ -670,7 +668,7 @@
};
const params = [unsignedEvent, getIndexOptions()];
const resp = await rpcCall("sign_event", params);
const resp = await rpcCall("nostr_sign_event", params);
signOutEl.textContent = pretty(resp?.result ?? resp);
} catch (e) {
signOutEl.textContent = String(e);
@@ -682,7 +680,7 @@
const peer = requirePeerHex(nip04PeerEl.value);
const msg = String(nip04MsgEl.value || "");
const params = [peer, msg, getIndexOptions()];
const resp = await rpcCall("nip04_encrypt", params);
const resp = await rpcCall("nostr_nip04_encrypt", params);
nip04OutEl.textContent = pretty(resp?.result ?? resp);
if (resp && typeof resp.result === "string") {
nip04DecPeerEl.value = peer;
@@ -698,7 +696,7 @@
const peer = requirePeerHex(nip04DecPeerEl.value);
const ciphertext = String(nip04CipherEl.value || "");
const params = [peer, ciphertext, getIndexOptions()];
const resp = await rpcCall("nip04_decrypt", params);
const resp = await rpcCall("nostr_nip04_decrypt", params);
nip04DecOutEl.textContent = requireStringResult(resp, "NIP-04 decrypt");
} catch (e) {
nip04DecOutEl.textContent = String(e);
@@ -710,7 +708,7 @@
const peer = requirePeerHex(nip44PeerEl.value);
const msg = String(nip44MsgEl.value || "");
const params = [peer, msg, getIndexOptions()];
const resp = await rpcCall("nip44_encrypt", params);
const resp = await rpcCall("nostr_nip44_encrypt", params);
nip44OutEl.textContent = pretty(resp?.result ?? resp);
if (resp && typeof resp.result === "string") {
nip44DecPeerEl.value = peer;
@@ -726,7 +724,7 @@
const peer = requirePeerHex(nip44DecPeerEl.value);
const ciphertext = String(nip44CipherEl.value || "");
const params = [peer, ciphertext, getIndexOptions()];
const resp = await rpcCall("nip44_decrypt", params);
const resp = await rpcCall("nostr_nip44_decrypt", params);
nip44DecOutEl.textContent = requireStringResult(resp, "NIP-44 decrypt");
} catch (e) {
nip44DecOutEl.textContent = String(e);
+7 -7
View File
@@ -369,7 +369,7 @@
async function fetchOwnPubkeyAndFillPeers() {
const params = [getIndexOptions()];
const resp = await rpcCall("get_public_key", params, "web-own-pubkey");
const resp = await rpcCall("nostr_get_public_key", params, "web-own-pubkey");
if (resp && typeof resp.result === "string") {
ownPubkey = resp.result.trim().toLowerCase();
pubkeyOutEl.textContent = pretty(resp);
@@ -383,7 +383,7 @@
}
async function connect() {
dev = await navigator.usb.requestDevice({ filters: [{ vendorId: 0x303a }] });
dev = await navigator.usb.requestDevice({ filters: [{}] });
await dev.open();
if (dev.configuration === null) {
await dev.selectConfiguration(1);
@@ -460,7 +460,7 @@
};
const params = [unsignedEvent, getIndexOptions()];
const resp = await rpcCall("sign_event", params, "web-sign-kind1");
const resp = await rpcCall("nostr_sign_event", params, "web-sign-kind1");
signOutEl.textContent = pretty(resp?.result ?? resp);
} catch (e) {
signOutEl.textContent = String(e);
@@ -472,7 +472,7 @@
const peer = requirePeerHex(nip04PeerEl.value);
const msg = String(nip04MsgEl.value || "");
const params = [peer, msg, getIndexOptions()];
const resp = await rpcCall("nip04_encrypt", params, "web-nip04-enc");
const resp = await rpcCall("nostr_nip04_encrypt", params, "web-nip04-enc");
nip04OutEl.textContent = pretty(resp?.result ?? resp);
if (resp && typeof resp.result === "string") {
nip04DecPeerEl.value = peer;
@@ -488,7 +488,7 @@
const peer = requirePeerHex(nip04DecPeerEl.value);
const ciphertext = String(nip04CipherEl.value || "");
const params = [peer, ciphertext, getIndexOptions()];
const resp = await rpcCall("nip04_decrypt", params, "web-nip04-dec");
const resp = await rpcCall("nostr_nip04_decrypt", params, "web-nip04-dec");
nip04DecOutEl.textContent = requireStringResult(resp, "NIP-04 decrypt");
} catch (e) {
nip04DecOutEl.textContent = String(e);
@@ -500,7 +500,7 @@
const peer = requirePeerHex(nip44PeerEl.value);
const msg = String(nip44MsgEl.value || "");
const params = [peer, msg, getIndexOptions()];
const resp = await rpcCall("nip44_encrypt", params, "web-nip44-enc");
const resp = await rpcCall("nostr_nip44_encrypt", params, "web-nip44-enc");
nip44OutEl.textContent = pretty(resp?.result ?? resp);
if (resp && typeof resp.result === "string") {
nip44DecPeerEl.value = peer;
@@ -516,7 +516,7 @@
const peer = requirePeerHex(nip44DecPeerEl.value);
const ciphertext = String(nip44CipherEl.value || "");
const params = [peer, ciphertext, getIndexOptions()];
const resp = await rpcCall("nip44_decrypt", params, "web-nip44-dec");
const resp = await rpcCall("nostr_nip44_decrypt", params, "web-nip44-dec");
nip44DecOutEl.textContent = requireStringResult(resp, "NIP-44 decrypt");
} catch (e) {
nip44DecOutEl.textContent = String(e);
+1 -1
View File
@@ -1 +1 @@
0.1.19
0.1.22
+2 -2
View File
@@ -2005,8 +2005,8 @@ class AuthManager {
}
const paired = await window.NSignerWebUSB.getPairedDevice({
vendorId: nsigner.deviceVid ?? 0x303a,
productId: nsigner.deviceProductId ?? 0x4001,
vendorId: nsigner.deviceVid ?? null,
productId: nsigner.deviceProductId ?? null,
serialNumber: nsigner.deviceSerial || undefined
});
+22 -21
View File
@@ -1,17 +1,15 @@
class NSignerWebSerial {
// CH340 VID:PID 0x1a86:0x7523 (most CYD revisions)
// CP2102 VID:PID 0x10c4:0xea60 (some CYD revisions)
static FILTERS = [
{ usbVendorId: 0x1a86, usbProductId: 0x7523 },
{ usbVendorId: 0x10c4, usbProductId: 0xea60 }
];
// Keep Web Serial chooser unfiltered so users can select any serial port.
static FILTERS = [];
static async requestAndConnect(options = {}) {
if (!('serial' in navigator)) {
throw new Error('Web Serial API not available in this browser (requires Chrome 89+, Edge 89+, or Brave)');
}
const port = await navigator.serial.requestPort({ filters: NSignerWebSerial.FILTERS });
const port = NSignerWebSerial.FILTERS.length
? await navigator.serial.requestPort({ filters: NSignerWebSerial.FILTERS })
: await navigator.serial.requestPort();
const driver = new NSignerWebSerial(port, options);
await driver.open();
return driver;
@@ -190,43 +188,43 @@ class NSignerWebSerial {
async getPublicKey() {
const params = [{ nostr_index: this.nostrIndex }];
const resp = await this._rpcCall('get_public_key', params);
const resp = await this._rpcCall('nostr_get_public_key', params);
if (!resp || typeof resp.result !== 'string') {
throw new Error('Invalid get_public_key response');
throw new Error('Invalid nostr_get_public_key response');
}
return resp.result.trim().toLowerCase();
}
async signEvent(unsignedEvent) {
const params = [unsignedEvent, { nostr_index: this.nostrIndex }];
const resp = await this._rpcCall('sign_event', params);
const resp = await this._rpcCall('nostr_sign_event', params);
if (!resp || typeof resp.result !== 'object') {
throw new Error('Invalid sign_event response');
throw new Error('Invalid nostr_sign_event response');
}
return resp.result;
}
async nip04Encrypt(peerHex, plaintext) {
const resp = await this._rpcCall('nip04_encrypt', [peerHex, plaintext, { nostr_index: this.nostrIndex }]);
if (!resp || typeof resp.result !== 'string') throw new Error('Invalid nip04_encrypt response');
const resp = await this._rpcCall('nostr_nip04_encrypt', [peerHex, plaintext, { nostr_index: this.nostrIndex }]);
if (!resp || typeof resp.result !== 'string') throw new Error('Invalid nostr_nip04_encrypt response');
return resp.result;
}
async nip04Decrypt(peerHex, ciphertext) {
const resp = await this._rpcCall('nip04_decrypt', [peerHex, ciphertext, { nostr_index: this.nostrIndex }]);
if (!resp || typeof resp.result !== 'string') throw new Error('Invalid nip04_decrypt response');
const resp = await this._rpcCall('nostr_nip04_decrypt', [peerHex, ciphertext, { nostr_index: this.nostrIndex }]);
if (!resp || typeof resp.result !== 'string') throw new Error('Invalid nostr_nip04_decrypt response');
return resp.result;
}
async nip44Encrypt(peerHex, plaintext) {
const resp = await this._rpcCall('nip44_encrypt', [peerHex, plaintext, { nostr_index: this.nostrIndex }]);
if (!resp || typeof resp.result !== 'string') throw new Error('Invalid nip44_encrypt response');
const resp = await this._rpcCall('nostr_nip44_encrypt', [peerHex, plaintext, { nostr_index: this.nostrIndex }]);
if (!resp || typeof resp.result !== 'string') throw new Error('Invalid nostr_nip44_encrypt response');
return resp.result;
}
async nip44Decrypt(peerHex, ciphertext) {
const resp = await this._rpcCall('nip44_decrypt', [peerHex, ciphertext, { nostr_index: this.nostrIndex }]);
if (!resp || typeof resp.result !== 'string') throw new Error('Invalid nip44_decrypt response');
const resp = await this._rpcCall('nostr_nip44_decrypt', [peerHex, ciphertext, { nostr_index: this.nostrIndex }]);
if (!resp || typeof resp.result !== 'string') throw new Error('Invalid nostr_nip44_decrypt response');
return resp.result;
}
@@ -442,12 +440,15 @@ class NSignerWebSerial {
const vid = matchInfo?.usbVendorId;
const pid = matchInfo?.usbProductId;
return ports.find((p) => {
// Prefer exact VID/PID match when available, but do not require it.
const exact = ports.find((p) => {
const i = p.getInfo?.() || {};
if (vid != null && i.usbVendorId !== vid) return false;
if (pid != null && i.usbProductId !== pid) return false;
return true;
}) || ports[0] || null;
});
return exact || ports[0] || null;
} catch (_) {
return null;
}
+19 -18
View File
@@ -1,7 +1,7 @@
class NSignerWebUSB {
// Keep picker broad (vendor-level) so Chromium can show all matching Feather states.
// Product-level checks still happen during open()/RPC.
static FILTERS = [{ vendorId: 0x303a }];
// Keep WebUSB chooser unfiltered so users can select any USB device.
// WebUSB requires a non-empty filters array; {} matches all devices.
static FILTERS = [{}];
static async requestAndConnect(options = {}) {
if (!('usb' in navigator)) {
@@ -20,13 +20,14 @@ class NSignerWebUSB {
const devices = await navigator.usb.getDevices();
if (!devices || devices.length === 0) return null;
const vendorId = options.vendorId ?? 0x303a;
const vendorId = options.vendorId ?? null;
const productId = options.productId ?? null;
const serial = options.serialNumber || null;
return devices.find((d) => {
const vendorMatch = d.vendorId === vendorId;
if (!vendorMatch) return false;
if (vendorId !== null && vendorId !== undefined && d.vendorId !== vendorId) {
return false;
}
if (productId !== null && productId !== undefined && d.productId !== productId) {
return false;
@@ -153,43 +154,43 @@ class NSignerWebUSB {
async getPublicKey() {
const params = [{ nostr_index: this.nostrIndex }];
const resp = await this._rpcCall('get_public_key', params);
const resp = await this._rpcCall('nostr_get_public_key', params);
if (!resp || typeof resp.result !== 'string') {
throw new Error('Invalid get_public_key response');
throw new Error('Invalid nostr_get_public_key response');
}
return resp.result.trim().toLowerCase();
}
async signEvent(unsignedEvent) {
const params = [unsignedEvent, { nostr_index: this.nostrIndex }];
const resp = await this._rpcCall('sign_event', params);
const resp = await this._rpcCall('nostr_sign_event', params);
if (!resp || typeof resp.result !== 'object') {
throw new Error('Invalid sign_event response');
throw new Error('Invalid nostr_sign_event response');
}
return resp.result;
}
async nip04Encrypt(peerHex, plaintext) {
const resp = await this._rpcCall('nip04_encrypt', [peerHex, plaintext, { nostr_index: this.nostrIndex }]);
if (!resp || typeof resp.result !== 'string') throw new Error('Invalid nip04_encrypt response');
const resp = await this._rpcCall('nostr_nip04_encrypt', [peerHex, plaintext, { nostr_index: this.nostrIndex }]);
if (!resp || typeof resp.result !== 'string') throw new Error('Invalid nostr_nip04_encrypt response');
return resp.result;
}
async nip04Decrypt(peerHex, ciphertext) {
const resp = await this._rpcCall('nip04_decrypt', [peerHex, ciphertext, { nostr_index: this.nostrIndex }]);
if (!resp || typeof resp.result !== 'string') throw new Error('Invalid nip04_decrypt response');
const resp = await this._rpcCall('nostr_nip04_decrypt', [peerHex, ciphertext, { nostr_index: this.nostrIndex }]);
if (!resp || typeof resp.result !== 'string') throw new Error('Invalid nostr_nip04_decrypt response');
return resp.result;
}
async nip44Encrypt(peerHex, plaintext) {
const resp = await this._rpcCall('nip44_encrypt', [peerHex, plaintext, { nostr_index: this.nostrIndex }]);
if (!resp || typeof resp.result !== 'string') throw new Error('Invalid nip44_encrypt response');
const resp = await this._rpcCall('nostr_nip44_encrypt', [peerHex, plaintext, { nostr_index: this.nostrIndex }]);
if (!resp || typeof resp.result !== 'string') throw new Error('Invalid nostr_nip44_encrypt response');
return resp.result;
}
async nip44Decrypt(peerHex, ciphertext) {
const resp = await this._rpcCall('nip44_decrypt', [peerHex, ciphertext, { nostr_index: this.nostrIndex }]);
if (!resp || typeof resp.result !== 'string') throw new Error('Invalid nip44_decrypt response');
const resp = await this._rpcCall('nostr_nip44_decrypt', [peerHex, ciphertext, { nostr_index: this.nostrIndex }]);
if (!resp || typeof resp.result !== 'string') throw new Error('Invalid nostr_nip44_decrypt response');
return resp.result;
}
+4 -7
View File
@@ -1351,7 +1351,7 @@ class Modal {
try {
// Incremental step 1:
// chooser + capture + driver open only (no getPublicKey/auth yet).
const selectedDevice = await navigator.usb.requestDevice({ filters: [{ vendorId: 0x303a }] });
const selectedDevice = await navigator.usb.requestDevice({ filters: [{}] });
this._nsignerSelectedDevice = selectedDevice;
if (!window.NSignerWebUSB || typeof window.NSignerWebUSB !== 'function') {
@@ -1696,10 +1696,7 @@ class Modal {
if (!driver && canUsb) {
try {
const pairedUsb = await window.NSignerWebUSB.getPairedDevice({
vendorId: 0x303a,
productId: 0x4001
});
const pairedUsb = await window.NSignerWebUSB.getPairedDevice();
if (pairedUsb) {
renderConnectingScreen('Reconnecting to paired WebUSB device...');
transport = 'webusb';
@@ -1743,7 +1740,7 @@ class Modal {
} else if (target === 'webusb') {
renderConnectingScreen('Waiting for WebUSB device selection...');
try {
const selectedDevice = await navigator.usb.requestDevice({ filters: [{ vendorId: 0x303a }] });
const selectedDevice = await navigator.usb.requestDevice({ filters: [{}] });
transport = 'webusb';
driver = await connectUsbFromDevice(selectedDevice);
} catch (usbErr) {
@@ -1988,7 +1985,7 @@ class Modal {
throw new Error('NSignerWebUSB driver missing');
}
const selectedDevice = await navigator.usb.requestDevice({ filters: [{ vendorId: 0x303a }] });
const selectedDevice = await navigator.usb.requestDevice({ filters: [{}] });
const driver = new window.NSignerWebUSB(selectedDevice, {
callerSecretKey: caller.secret,