2 Commits

Author SHA1 Message Date
Your Name
77ea4a8e67 cleaned up visuals 2025-09-15 14:52:21 -04:00
Your Name
12d4810f4c login with exposed api for web page fixed. 2025-09-15 14:24:14 -04:00
3 changed files with 559 additions and 95 deletions

View File

@@ -675,7 +675,7 @@ class FloatingTab {
appearance: {
style: 'pill', // 'pill', 'square', 'circle'
theme: 'auto', // 'auto', 'light', 'dark'
icon: '[LOGIN]',
icon: '',
text: 'Login',
iconOnly: false
},
@@ -685,11 +685,14 @@ class FloatingTab {
autoSlide: true,
persistent: false
},
getUserInfo: false,
getUserRelay: [],
...options
};
this.isAuthenticated = false;
this.userInfo = null;
this.userProfile = null;
this.container = null;
this.isVisible = false;
@@ -790,11 +793,24 @@ class FloatingTab {
}
}
_handleAuth(authData) {
async _handleAuth(authData) {
console.log('FloatingTab: Handling authentication:', authData);
this.isAuthenticated = true;
this.userInfo = authData;
// Fetch user profile if enabled and we have a pubkey
if (this.options.getUserInfo && authData.pubkey) {
console.log('FloatingTab: Fetching user profile for:', authData.pubkey);
try {
const profile = await this._fetchUserProfile(authData.pubkey);
this.userProfile = profile;
console.log('FloatingTab: User profile fetched:', profile);
} catch (error) {
console.warn('FloatingTab: Failed to fetch user profile:', error);
this.userProfile = null;
}
}
if (this.options.behavior.hideWhenAuthenticated) {
this.hide();
} else {
@@ -806,6 +822,7 @@ class FloatingTab {
console.log('FloatingTab: Handling logout');
this.isAuthenticated = false;
this.userInfo = null;
this.userProfile = null;
if (this.options.behavior.hideWhenAuthenticated) {
this.show();
@@ -873,18 +890,29 @@ class FloatingTab {
// Update content
if (this.isAuthenticated && this.options.behavior.showUserInfo) {
const display = this.userInfo?.pubkey ?
(this.options.appearance.iconOnly ?
'[USER]' :
\`[USER] \${this.userInfo.pubkey.slice(0, 6)}...\`) :
(this.options.appearance.iconOnly ? '[AUTH]' : '[AUTH] Logged In');
let display;
// Use profile name if available, otherwise fall back to pubkey
if (this.userProfile?.name || this.userProfile?.display_name) {
const userName = this.userProfile.name || this.userProfile.display_name;
display = this.options.appearance.iconOnly
? userName.slice(0, 8)
: userName;
} else if (this.userInfo?.pubkey) {
// Fallback to pubkey display
display = this.options.appearance.iconOnly
? this.userInfo.pubkey.slice(0, 6)
: \`\${this.userInfo.pubkey.slice(0, 6)}...\`;
} else {
display = this.options.appearance.iconOnly ? 'User' : 'Authenticated';
}
this.container.textContent = display;
this.container.className = 'nl-floating-tab nl-floating-tab--logged-in';
} else {
const display = this.options.appearance.iconOnly ?
this.options.appearance.icon :
\`\${this.options.appearance.icon} \${this.options.appearance.text}\`;
(this.options.appearance.icon ? \`\${this.options.appearance.icon} \${this.options.appearance.text}\` : this.options.appearance.text);
this.container.textContent = display;
this.container.className = 'nl-floating-tab nl-floating-tab--logged-out';
@@ -915,6 +943,62 @@ class FloatingTab {
}
}
async _fetchUserProfile(pubkey) {
if (!this.options.getUserInfo) {
console.log('FloatingTab: getUserInfo disabled, skipping profile fetch');
return null;
}
// Determine which relays to use
const relays = this.options.getUserRelay.length > 0
? this.options.getUserRelay
: (this.modal?.options?.relays || ['wss://relay.damus.io', 'wss://nos.lol']);
console.log('FloatingTab: Fetching profile from relays:', relays);
try {
// Create a SimplePool instance for querying
const pool = new window.NostrTools.nip46.SimplePool();
// Query for kind 0 (user metadata) events
const events = await pool.querySync(relays, {
kinds: [0],
authors: [pubkey],
limit: 1
}, { timeout: 5000 });
console.log('FloatingTab: Profile query returned', events.length, 'events');
if (events.length === 0) {
console.log('FloatingTab: No profile events found');
return null;
}
// Get the most recent event
const latestEvent = events.sort((a, b) => b.created_at - a.created_at)[0];
try {
const profile = JSON.parse(latestEvent.content);
console.log('FloatingTab: Parsed profile:', profile);
// Return relevant profile fields
return {
name: profile.name || null,
display_name: profile.display_name || null,
about: profile.about || null,
picture: profile.picture || null,
nip05: profile.nip05 || null
};
} catch (parseError) {
console.warn('FloatingTab: Failed to parse profile JSON:', parseError);
return null;
}
} catch (error) {
console.error('FloatingTab: Profile fetch error:', error);
return null;
}
}
_position() {
if (!this.container) return;
@@ -1076,7 +1160,7 @@ class NostrLite {
appearance: {
style: 'pill',
theme: 'auto',
icon: '[LOGIN]',
icon: '',
text: 'Login',
iconOnly: false
},
@@ -1085,7 +1169,9 @@ class NostrLite {
showUserInfo: true,
autoSlide: true,
persistent: false
}
},
getUserInfo: false,
getUserRelay: []
},
...options
};
@@ -1254,13 +1340,13 @@ class WindowNostr {
if (event.detail.method === 'extension') {
this.authenticatedExtension = event.detail.extension;
console.log('WindowNostr: Captured authenticated extension:', this.authenticatedExtension?.constructor?.name);
// Re-install our facade to ensure we intercept signEvent calls
// Extensions may overwrite window.nostr after authentication
if (typeof window !== 'undefined') {
console.log('WindowNostr: Re-installing facade after authentication');
window.nostr = this;
}
}
// CRITICAL FIX: Re-install our facade for ALL authentication methods
// Extensions may overwrite window.nostr after ANY authentication, not just extension auth
if (typeof window !== 'undefined') {
console.log('WindowNostr: Re-installing facade after', this.authState?.method, 'authentication');
window.nostr = this;
}
console.log('WindowNostr: Auth state updated:', this.authState?.method);
@@ -1270,6 +1356,12 @@ class WindowNostr {
this.authState = null;
this.authenticatedExtension = null;
console.log('WindowNostr: Auth state cleared');
// Re-install facade after logout to ensure we maintain control
if (typeof window !== 'undefined') {
console.log('WindowNostr: Re-installing facade after logout');
window.nostr = this;
}
});
}
}

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: 2025-09-15T17:48:16.817Z
* Generated on: 2025-09-15T18:50:50.789Z
*/
// Verify dependencies are loaded
@@ -1076,26 +1076,6 @@ class Modal {
button.style.background = 'var(--nl-secondary-color)';
};
const iconDiv = document.createElement('div');
// Replace emoji icons with text-based ones
const iconMap = {
'🔌': '[EXT]',
'🔑': '[KEY]',
'🌐': '[NET]',
'👁️': '[VIEW]',
'📱': '[SMS]'
};
iconDiv.textContent = iconMap[option.icon] || option.icon;
iconDiv.style.cssText = `
font-size: 16px;
font-weight: bold;
margin-right: 16px;
width: 50px;
text-align: center;
color: var(--nl-primary-color);
font-family: var(--nl-font-family, 'Courier New', monospace);
`;
const contentDiv = document.createElement('div');
contentDiv.style.cssText = 'flex: 1; text-align: left;';
@@ -1119,7 +1099,6 @@ class Modal {
contentDiv.appendChild(titleDiv);
contentDiv.appendChild(descDiv);
button.appendChild(iconDiv);
button.appendChild(contentDiv);
this.modalBody.appendChild(button);
});
@@ -1627,15 +1606,176 @@ class Modal {
const warningDiv = document.createElement('div');
warningDiv.textContent = '⚠️ Save your secret key securely!';
warningDiv.style.cssText = 'background: #fef3c7; color: #92400e; padding: 12px; border-radius: 6px; margin-bottom: 16px; font-size: 14px;';
warningDiv.style.cssText = 'background: #fef3c7; color: #92400e; padding: 12px; border-radius: 6px; margin-bottom: 16px; font-size: 12px;';
const nsecDiv = document.createElement('div');
nsecDiv.innerHTML = `<strong>Your Secret Key:</strong><br><code style="word-break: break-all; background: #f3f4f6; padding: 8px; border-radius: 4px;">${nsec}</code>`;
nsecDiv.style.cssText = 'margin-bottom: 16px; font-size: 14px;';
// Helper function to create copy button
const createCopyButton = (text, label) => {
const copyBtn = document.createElement('button');
copyBtn.textContent = `Copy ${label}`;
copyBtn.style.cssText = `
margin-left: 8px;
padding: 4px 8px;
font-size: 10px;
background: var(--nl-secondary-color);
color: var(--nl-primary-color);
border: 1px solid var(--nl-primary-color);
border-radius: 4px;
cursor: pointer;
font-family: var(--nl-font-family, 'Courier New', monospace);
`;
copyBtn.onclick = async (e) => {
e.preventDefault();
try {
await navigator.clipboard.writeText(text);
const originalText = copyBtn.textContent;
copyBtn.textContent = '✓ Copied!';
copyBtn.style.color = '#059669';
setTimeout(() => {
copyBtn.textContent = originalText;
copyBtn.style.color = 'var(--nl-primary-color)';
}, 2000);
} catch (err) {
console.error('Failed to copy:', err);
copyBtn.textContent = '✗ Failed';
copyBtn.style.color = '#dc2626';
setTimeout(() => {
copyBtn.textContent = originalText;
copyBtn.style.color = 'var(--nl-primary-color)';
}, 2000);
}
};
return copyBtn;
};
const npubDiv = document.createElement('div');
npubDiv.innerHTML = `<strong>Your Public Key:</strong><br><code style="word-break: break-all; background: #f3f4f6; padding: 8px; border-radius: 4px;">${window.NostrTools.nip19.npubEncode(pubkey)}</code>`;
npubDiv.style.cssText = 'margin-bottom: 16px; font-size: 14px;';
// Convert pubkey to hex for verification
const pubkeyHex = typeof pubkey === 'string' ? pubkey : Array.from(pubkey).map(b => b.toString(16).padStart(2, '0')).join('');
// Decode nsec to get secret key as hex
let secretKeyHex = '';
try {
const decoded = window.NostrTools.nip19.decode(nsec);
secretKeyHex = Array.from(decoded.data).map(b => b.toString(16).padStart(2, '0')).join('');
} catch (err) {
console.error('Failed to decode nsec for hex display:', err);
}
// Secret Key Section
const nsecSection = document.createElement('div');
nsecSection.style.cssText = 'margin-bottom: 16px;';
const nsecLabel = document.createElement('div');
nsecLabel.innerHTML = '<strong>Your Secret Key (nsec):</strong>';
nsecLabel.style.cssText = 'margin-bottom: 4px; font-size: 12px; font-weight: 600;';
const nsecContainer = document.createElement('div');
nsecContainer.style.cssText = 'display: flex; align-items: flex-start; margin-bottom: 8px;';
const nsecCode = document.createElement('code');
nsecCode.textContent = nsec;
nsecCode.style.cssText = `
flex: 1;
word-break: break-all;
background: #f3f4f6;
padding: 6px;
border-radius: 4px;
font-size: 10px;
line-height: 1.3;
font-family: 'Courier New', monospace;
display: block;
`;
nsecContainer.appendChild(nsecCode);
nsecContainer.appendChild(createCopyButton(nsec, 'nsec'));
nsecSection.appendChild(nsecLabel);
nsecSection.appendChild(nsecContainer);
// Secret Key Hex Section
if (secretKeyHex) {
const secretHexLabel = document.createElement('div');
secretHexLabel.innerHTML = '<strong>Secret Key (hex):</strong>';
secretHexLabel.style.cssText = 'margin-bottom: 4px; font-size: 12px; font-weight: 600;';
const secretHexContainer = document.createElement('div');
secretHexContainer.style.cssText = 'display: flex; align-items: flex-start; margin-bottom: 8px;';
const secretHexCode = document.createElement('code');
secretHexCode.textContent = secretKeyHex;
secretHexCode.style.cssText = `
flex: 1;
word-break: break-all;
background: #f3f4f6;
padding: 6px;
border-radius: 4px;
font-size: 10px;
line-height: 1.3;
font-family: 'Courier New', monospace;
display: block;
`;
secretHexContainer.appendChild(secretHexCode);
secretHexContainer.appendChild(createCopyButton(secretKeyHex, 'hex'));
nsecSection.appendChild(secretHexLabel);
nsecSection.appendChild(secretHexContainer);
}
// Public Key Section
const npubSection = document.createElement('div');
npubSection.style.cssText = 'margin-bottom: 16px;';
const npub = window.NostrTools.nip19.npubEncode(pubkey);
const npubLabel = document.createElement('div');
npubLabel.innerHTML = '<strong>Your Public Key (npub):</strong>';
npubLabel.style.cssText = 'margin-bottom: 4px; font-size: 12px; font-weight: 600;';
const npubContainer = document.createElement('div');
npubContainer.style.cssText = 'display: flex; align-items: flex-start; margin-bottom: 8px;';
const npubCode = document.createElement('code');
npubCode.textContent = npub;
npubCode.style.cssText = `
flex: 1;
word-break: break-all;
background: #f3f4f6;
padding: 6px;
border-radius: 4px;
font-size: 10px;
line-height: 1.3;
font-family: 'Courier New', monospace;
display: block;
`;
npubContainer.appendChild(npubCode);
npubContainer.appendChild(createCopyButton(npub, 'npub'));
npubSection.appendChild(npubLabel);
npubSection.appendChild(npubContainer);
// Public Key Hex Section
const pubHexLabel = document.createElement('div');
pubHexLabel.innerHTML = '<strong>Public Key (hex):</strong>';
pubHexLabel.style.cssText = 'margin-bottom: 4px; font-size: 12px; font-weight: 600;';
const pubHexContainer = document.createElement('div');
pubHexContainer.style.cssText = 'display: flex; align-items: flex-start;';
const pubHexCode = document.createElement('code');
pubHexCode.textContent = pubkeyHex;
pubHexCode.style.cssText = `
flex: 1;
word-break: break-all;
background: #f3f4f6;
padding: 6px;
border-radius: 4px;
font-size: 10px;
line-height: 1.3;
font-family: 'Courier New', monospace;
display: block;
`;
pubHexContainer.appendChild(pubHexCode);
pubHexContainer.appendChild(createCopyButton(pubkeyHex, 'hex'));
npubSection.appendChild(pubHexLabel);
npubSection.appendChild(pubHexContainer);
const continueButton = document.createElement('button');
continueButton.textContent = 'Continue';
@@ -1644,8 +1784,8 @@ class Modal {
this.modalBody.appendChild(title);
this.modalBody.appendChild(warningDiv);
this.modalBody.appendChild(nsecDiv);
this.modalBody.appendChild(npubDiv);
this.modalBody.appendChild(nsecSection);
this.modalBody.appendChild(npubSection);
this.modalBody.appendChild(continueButton);
}
@@ -1983,7 +2123,7 @@ class FloatingTab {
appearance: {
style: 'pill', // 'pill', 'square', 'circle'
theme: 'auto', // 'auto', 'light', 'dark'
icon: '[LOGIN]',
icon: '',
text: 'Login',
iconOnly: false
},
@@ -1993,11 +2133,14 @@ class FloatingTab {
autoSlide: true,
persistent: false
},
getUserInfo: false,
getUserRelay: [],
...options
};
this.isAuthenticated = false;
this.userInfo = null;
this.userProfile = null;
this.container = null;
this.isVisible = false;
@@ -2098,11 +2241,24 @@ class FloatingTab {
}
}
_handleAuth(authData) {
async _handleAuth(authData) {
console.log('FloatingTab: Handling authentication:', authData);
this.isAuthenticated = true;
this.userInfo = authData;
// Fetch user profile if enabled and we have a pubkey
if (this.options.getUserInfo && authData.pubkey) {
console.log('FloatingTab: Fetching user profile for:', authData.pubkey);
try {
const profile = await this._fetchUserProfile(authData.pubkey);
this.userProfile = profile;
console.log('FloatingTab: User profile fetched:', profile);
} catch (error) {
console.warn('FloatingTab: Failed to fetch user profile:', error);
this.userProfile = null;
}
}
if (this.options.behavior.hideWhenAuthenticated) {
this.hide();
} else {
@@ -2114,6 +2270,7 @@ class FloatingTab {
console.log('FloatingTab: Handling logout');
this.isAuthenticated = false;
this.userInfo = null;
this.userProfile = null;
if (this.options.behavior.hideWhenAuthenticated) {
this.show();
@@ -2181,18 +2338,29 @@ class FloatingTab {
// Update content
if (this.isAuthenticated && this.options.behavior.showUserInfo) {
const display = this.userInfo?.pubkey ?
(this.options.appearance.iconOnly ?
'[USER]' :
`[USER] ${this.userInfo.pubkey.slice(0, 6)}...`) :
(this.options.appearance.iconOnly ? '[AUTH]' : '[AUTH] Logged In');
let display;
// Use profile name if available, otherwise fall back to pubkey
if (this.userProfile?.name || this.userProfile?.display_name) {
const userName = this.userProfile.name || this.userProfile.display_name;
display = this.options.appearance.iconOnly
? userName.slice(0, 8)
: userName;
} else if (this.userInfo?.pubkey) {
// Fallback to pubkey display
display = this.options.appearance.iconOnly
? this.userInfo.pubkey.slice(0, 6)
: `${this.userInfo.pubkey.slice(0, 6)}...`;
} else {
display = this.options.appearance.iconOnly ? 'User' : 'Authenticated';
}
this.container.textContent = display;
this.container.className = 'nl-floating-tab nl-floating-tab--logged-in';
} else {
const display = this.options.appearance.iconOnly ?
this.options.appearance.icon :
`${this.options.appearance.icon} ${this.options.appearance.text}`;
(this.options.appearance.icon ? `${this.options.appearance.icon} ${this.options.appearance.text}` : this.options.appearance.text);
this.container.textContent = display;
this.container.className = 'nl-floating-tab nl-floating-tab--logged-out';
@@ -2223,6 +2391,62 @@ class FloatingTab {
}
}
async _fetchUserProfile(pubkey) {
if (!this.options.getUserInfo) {
console.log('FloatingTab: getUserInfo disabled, skipping profile fetch');
return null;
}
// Determine which relays to use
const relays = this.options.getUserRelay.length > 0
? this.options.getUserRelay
: (this.modal?.options?.relays || ['wss://relay.damus.io', 'wss://nos.lol']);
console.log('FloatingTab: Fetching profile from relays:', relays);
try {
// Create a SimplePool instance for querying
const pool = new window.NostrTools.nip46.SimplePool();
// Query for kind 0 (user metadata) events
const events = await pool.querySync(relays, {
kinds: [0],
authors: [pubkey],
limit: 1
}, { timeout: 5000 });
console.log('FloatingTab: Profile query returned', events.length, 'events');
if (events.length === 0) {
console.log('FloatingTab: No profile events found');
return null;
}
// Get the most recent event
const latestEvent = events.sort((a, b) => b.created_at - a.created_at)[0];
try {
const profile = JSON.parse(latestEvent.content);
console.log('FloatingTab: Parsed profile:', profile);
// Return relevant profile fields
return {
name: profile.name || null,
display_name: profile.display_name || null,
about: profile.about || null,
picture: profile.picture || null,
nip05: profile.nip05 || null
};
} catch (parseError) {
console.warn('FloatingTab: Failed to parse profile JSON:', parseError);
return null;
}
} catch (error) {
console.error('FloatingTab: Profile fetch error:', error);
return null;
}
}
_position() {
if (!this.container) return;
@@ -2384,7 +2608,7 @@ class NostrLite {
appearance: {
style: 'pill',
theme: 'auto',
icon: '[LOGIN]',
icon: '',
text: 'Login',
iconOnly: false
},
@@ -2393,7 +2617,9 @@ class NostrLite {
showUserInfo: true,
autoSlide: true,
persistent: false
}
},
getUserInfo: false,
getUserRelay: []
},
...options
};
@@ -2562,13 +2788,13 @@ class WindowNostr {
if (event.detail.method === 'extension') {
this.authenticatedExtension = event.detail.extension;
console.log('WindowNostr: Captured authenticated extension:', this.authenticatedExtension?.constructor?.name);
// Re-install our facade to ensure we intercept signEvent calls
// Extensions may overwrite window.nostr after authentication
if (typeof window !== 'undefined') {
console.log('WindowNostr: Re-installing facade after authentication');
window.nostr = this;
}
}
// CRITICAL FIX: Re-install our facade for ALL authentication methods
// Extensions may overwrite window.nostr after ANY authentication, not just extension auth
if (typeof window !== 'undefined') {
console.log('WindowNostr: Re-installing facade after', this.authState?.method, 'authentication');
window.nostr = this;
}
console.log('WindowNostr: Auth state updated:', this.authState?.method);
@@ -2578,6 +2804,12 @@ class WindowNostr {
this.authState = null;
this.authenticatedExtension = null;
console.log('WindowNostr: Auth state cleared');
// Re-install facade after logout to ensure we maintain control
if (typeof window !== 'undefined') {
console.log('WindowNostr: Re-installing facade after logout');
window.nostr = this;
}
});
}
}

View File

@@ -280,26 +280,6 @@ class Modal {
button.style.background = 'var(--nl-secondary-color)';
};
const iconDiv = document.createElement('div');
// Replace emoji icons with text-based ones
const iconMap = {
'🔌': '[EXT]',
'🔑': '[KEY]',
'🌐': '[NET]',
'👁️': '[VIEW]',
'📱': '[SMS]'
};
iconDiv.textContent = iconMap[option.icon] || option.icon;
iconDiv.style.cssText = `
font-size: 16px;
font-weight: bold;
margin-right: 16px;
width: 50px;
text-align: center;
color: var(--nl-primary-color);
font-family: var(--nl-font-family, 'Courier New', monospace);
`;
const contentDiv = document.createElement('div');
contentDiv.style.cssText = 'flex: 1; text-align: left;';
@@ -323,7 +303,6 @@ class Modal {
contentDiv.appendChild(titleDiv);
contentDiv.appendChild(descDiv);
button.appendChild(iconDiv);
button.appendChild(contentDiv);
this.modalBody.appendChild(button);
});
@@ -831,15 +810,176 @@ class Modal {
const warningDiv = document.createElement('div');
warningDiv.textContent = '⚠️ Save your secret key securely!';
warningDiv.style.cssText = 'background: #fef3c7; color: #92400e; padding: 12px; border-radius: 6px; margin-bottom: 16px; font-size: 14px;';
warningDiv.style.cssText = 'background: #fef3c7; color: #92400e; padding: 12px; border-radius: 6px; margin-bottom: 16px; font-size: 12px;';
const nsecDiv = document.createElement('div');
nsecDiv.innerHTML = `<strong>Your Secret Key:</strong><br><code style="word-break: break-all; background: #f3f4f6; padding: 8px; border-radius: 4px;">${nsec}</code>`;
nsecDiv.style.cssText = 'margin-bottom: 16px; font-size: 14px;';
// Helper function to create copy button
const createCopyButton = (text, label) => {
const copyBtn = document.createElement('button');
copyBtn.textContent = `Copy ${label}`;
copyBtn.style.cssText = `
margin-left: 8px;
padding: 4px 8px;
font-size: 10px;
background: var(--nl-secondary-color);
color: var(--nl-primary-color);
border: 1px solid var(--nl-primary-color);
border-radius: 4px;
cursor: pointer;
font-family: var(--nl-font-family, 'Courier New', monospace);
`;
copyBtn.onclick = async (e) => {
e.preventDefault();
try {
await navigator.clipboard.writeText(text);
const originalText = copyBtn.textContent;
copyBtn.textContent = '✓ Copied!';
copyBtn.style.color = '#059669';
setTimeout(() => {
copyBtn.textContent = originalText;
copyBtn.style.color = 'var(--nl-primary-color)';
}, 2000);
} catch (err) {
console.error('Failed to copy:', err);
copyBtn.textContent = '✗ Failed';
copyBtn.style.color = '#dc2626';
setTimeout(() => {
copyBtn.textContent = originalText;
copyBtn.style.color = 'var(--nl-primary-color)';
}, 2000);
}
};
return copyBtn;
};
const npubDiv = document.createElement('div');
npubDiv.innerHTML = `<strong>Your Public Key:</strong><br><code style="word-break: break-all; background: #f3f4f6; padding: 8px; border-radius: 4px;">${window.NostrTools.nip19.npubEncode(pubkey)}</code>`;
npubDiv.style.cssText = 'margin-bottom: 16px; font-size: 14px;';
// Convert pubkey to hex for verification
const pubkeyHex = typeof pubkey === 'string' ? pubkey : Array.from(pubkey).map(b => b.toString(16).padStart(2, '0')).join('');
// Decode nsec to get secret key as hex
let secretKeyHex = '';
try {
const decoded = window.NostrTools.nip19.decode(nsec);
secretKeyHex = Array.from(decoded.data).map(b => b.toString(16).padStart(2, '0')).join('');
} catch (err) {
console.error('Failed to decode nsec for hex display:', err);
}
// Secret Key Section
const nsecSection = document.createElement('div');
nsecSection.style.cssText = 'margin-bottom: 16px;';
const nsecLabel = document.createElement('div');
nsecLabel.innerHTML = '<strong>Your Secret Key (nsec):</strong>';
nsecLabel.style.cssText = 'margin-bottom: 4px; font-size: 12px; font-weight: 600;';
const nsecContainer = document.createElement('div');
nsecContainer.style.cssText = 'display: flex; align-items: flex-start; margin-bottom: 8px;';
const nsecCode = document.createElement('code');
nsecCode.textContent = nsec;
nsecCode.style.cssText = `
flex: 1;
word-break: break-all;
background: #f3f4f6;
padding: 6px;
border-radius: 4px;
font-size: 10px;
line-height: 1.3;
font-family: 'Courier New', monospace;
display: block;
`;
nsecContainer.appendChild(nsecCode);
nsecContainer.appendChild(createCopyButton(nsec, 'nsec'));
nsecSection.appendChild(nsecLabel);
nsecSection.appendChild(nsecContainer);
// Secret Key Hex Section
if (secretKeyHex) {
const secretHexLabel = document.createElement('div');
secretHexLabel.innerHTML = '<strong>Secret Key (hex):</strong>';
secretHexLabel.style.cssText = 'margin-bottom: 4px; font-size: 12px; font-weight: 600;';
const secretHexContainer = document.createElement('div');
secretHexContainer.style.cssText = 'display: flex; align-items: flex-start; margin-bottom: 8px;';
const secretHexCode = document.createElement('code');
secretHexCode.textContent = secretKeyHex;
secretHexCode.style.cssText = `
flex: 1;
word-break: break-all;
background: #f3f4f6;
padding: 6px;
border-radius: 4px;
font-size: 10px;
line-height: 1.3;
font-family: 'Courier New', monospace;
display: block;
`;
secretHexContainer.appendChild(secretHexCode);
secretHexContainer.appendChild(createCopyButton(secretKeyHex, 'hex'));
nsecSection.appendChild(secretHexLabel);
nsecSection.appendChild(secretHexContainer);
}
// Public Key Section
const npubSection = document.createElement('div');
npubSection.style.cssText = 'margin-bottom: 16px;';
const npub = window.NostrTools.nip19.npubEncode(pubkey);
const npubLabel = document.createElement('div');
npubLabel.innerHTML = '<strong>Your Public Key (npub):</strong>';
npubLabel.style.cssText = 'margin-bottom: 4px; font-size: 12px; font-weight: 600;';
const npubContainer = document.createElement('div');
npubContainer.style.cssText = 'display: flex; align-items: flex-start; margin-bottom: 8px;';
const npubCode = document.createElement('code');
npubCode.textContent = npub;
npubCode.style.cssText = `
flex: 1;
word-break: break-all;
background: #f3f4f6;
padding: 6px;
border-radius: 4px;
font-size: 10px;
line-height: 1.3;
font-family: 'Courier New', monospace;
display: block;
`;
npubContainer.appendChild(npubCode);
npubContainer.appendChild(createCopyButton(npub, 'npub'));
npubSection.appendChild(npubLabel);
npubSection.appendChild(npubContainer);
// Public Key Hex Section
const pubHexLabel = document.createElement('div');
pubHexLabel.innerHTML = '<strong>Public Key (hex):</strong>';
pubHexLabel.style.cssText = 'margin-bottom: 4px; font-size: 12px; font-weight: 600;';
const pubHexContainer = document.createElement('div');
pubHexContainer.style.cssText = 'display: flex; align-items: flex-start;';
const pubHexCode = document.createElement('code');
pubHexCode.textContent = pubkeyHex;
pubHexCode.style.cssText = `
flex: 1;
word-break: break-all;
background: #f3f4f6;
padding: 6px;
border-radius: 4px;
font-size: 10px;
line-height: 1.3;
font-family: 'Courier New', monospace;
display: block;
`;
pubHexContainer.appendChild(pubHexCode);
pubHexContainer.appendChild(createCopyButton(pubkeyHex, 'hex'));
npubSection.appendChild(pubHexLabel);
npubSection.appendChild(pubHexContainer);
const continueButton = document.createElement('button');
continueButton.textContent = 'Continue';
@@ -848,8 +988,8 @@ class Modal {
this.modalBody.appendChild(title);
this.modalBody.appendChild(warningDiv);
this.modalBody.appendChild(nsecDiv);
this.modalBody.appendChild(npubDiv);
this.modalBody.appendChild(nsecSection);
this.modalBody.appendChild(npubSection);
this.modalBody.appendChild(continueButton);
}