From 4505167246d1a476cb3a65ae1b75dbc71c65101a Mon Sep 17 00:00:00 2001 From: Your Name Date: Sun, 21 Sep 2025 11:51:33 -0400 Subject: [PATCH] Change key entry --- deploy.sh | 2 +- lite/VERSION | 2 +- lite/nostr-lite.js | 165 +++++++++++++++++++++++++++------------------ lite/ui/modal.js | 161 ++++++++++++++++++++++++++----------------- 4 files changed, 202 insertions(+), 128 deletions(-) diff --git a/deploy.sh b/deploy.sh index 679e015..29197cd 100755 --- a/deploy.sh +++ b/deploy.sh @@ -1,3 +1,3 @@ #!/bin/bash -rsync -avz --progress lite/{nostr-lite.js,nostr.bundle.js} ubuntu@laantungir.net:WWW/nostr-login-lite/ +rsync -avz --chmod=644 --progress lite/{nostr-lite.js,nostr.bundle.js} ubuntu@laantungir.net:WWW/nostr-login-lite/ diff --git a/lite/VERSION b/lite/VERSION index b1e80bb..845639e 100644 --- a/lite/VERSION +++ b/lite/VERSION @@ -1 +1 @@ -0.1.3 +0.1.4 diff --git a/lite/nostr-lite.js b/lite/nostr-lite.js index e02bfc0..9dc515b 100644 --- a/lite/nostr-lite.js +++ b/lite/nostr-lite.js @@ -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-21T15:22:26.408Z + * Generated on: 2025-09-21T15:51:33.328Z */ // Verify dependencies are loaded @@ -381,7 +381,7 @@ class Modal { `; const modalTitle = document.createElement('h2'); - modalTitle.textContent = 'Nostr Login v0.1.3'; + modalTitle.textContent = 'Nostr Login v0.1.4'; modalTitle.style.cssText = ` margin: 0; font-size: 24px; @@ -997,62 +997,8 @@ class Modal { _showLocalKeyScreen() { this.modalBody.innerHTML = ''; - const title = document.createElement('h3'); - title.textContent = 'Local Key'; - title.style.cssText = 'margin: 0 0 20px 0; font-size: 18px; font-weight: 600;'; - - const createButton = document.createElement('button'); - createButton.textContent = 'Create New Key'; - createButton.onclick = () => this._createLocalKey(); - createButton.style.cssText = this._getButtonStyle(); - - const importButton = document.createElement('button'); - importButton.textContent = 'Import Existing Key'; - importButton.onclick = () => this._showImportKeyForm(); - importButton.style.cssText = this._getButtonStyle() + 'margin-top: 12px;'; - - const backButton = document.createElement('button'); - backButton.textContent = 'Back'; - backButton.onclick = () => this._renderLoginOptions(); - backButton.style.cssText = ` - display: block; - margin-top: 20px; - padding: 12px; - background: #6b7280; - color: white; - border: none; - border-radius: 6px; - cursor: pointer; - `; - - this.modalBody.appendChild(title); - this.modalBody.appendChild(createButton); - this.modalBody.appendChild(importButton); - this.modalBody.appendChild(backButton); - } - - _createLocalKey() { - try { - const sk = window.NostrTools.generateSecretKey(); - const pk = window.NostrTools.getPublicKey(sk); - const nsec = window.NostrTools.nip19.nsecEncode(sk); - const npub = window.NostrTools.nip19.npubEncode(pk); - - this._showKeyDisplay(pk, nsec, 'created'); - } catch (error) { - this._showError('Failed to create key: ' + error.message); - } - } - - _showImportKeyForm() { - this.modalBody.innerHTML = ''; - - const title = document.createElement('h3'); - title.textContent = 'Import Local Key'; - title.style.cssText = 'margin: 0 0 16px 0; font-size: 18px; font-weight: 600;'; - const description = document.createElement('p'); - description.textContent = 'Enter your secret key in either nsec or hex format:'; + description.innerHTML = 'Enter your secret key in nsec or hex format, or generate new.'; description.style.cssText = 'margin-bottom: 12px; color: #6b7280; font-size: 14px;'; const textarea = document.createElement('textarea'); @@ -1074,10 +1020,40 @@ class Modal { const formatHint = document.createElement('div'); formatHint.style.cssText = 'margin-bottom: 16px; font-size: 12px; color: #6b7280; min-height: 16px;'; + const importButton = document.createElement('button'); + importButton.textContent = 'Import Key'; + importButton.disabled = true; + importButton.onclick = () => { + if (!importButton.disabled) { + this._importLocalKey(textarea.value); + } + }; + + // Set initial disabled state + importButton.style.cssText = ` + display: block; + width: 100%; + padding: 12px; + border: var(--nl-border-width) solid var(--nl-muted-color); + border-radius: var(--nl-border-radius); + font-size: 16px; + font-weight: 500; + cursor: not-allowed; + transition: all 0.2s; + font-family: var(--nl-font-family, 'Courier New', monospace); + background: var(--nl-secondary-color); + color: var(--nl-muted-color); + `; + textarea.oninput = () => { const value = textarea.value.trim(); if (!value) { formatHint.textContent = ''; + // Disable button + importButton.disabled = true; + importButton.style.borderColor = 'var(--nl-muted-color)'; + importButton.style.color = 'var(--nl-muted-color)'; + importButton.style.cursor = 'not-allowed'; return; } @@ -1085,33 +1061,94 @@ class Modal { if (format === 'nsec') { formatHint.textContent = '✅ Valid nsec format detected'; formatHint.style.color = '#059669'; + // Enable button + importButton.disabled = false; + importButton.style.borderColor = 'var(--nl-primary-color)'; + importButton.style.color = 'var(--nl-primary-color)'; + importButton.style.cursor = 'pointer'; } else if (format === 'hex') { formatHint.textContent = '✅ Valid hex format detected'; formatHint.style.color = '#059669'; + // Enable button + importButton.disabled = false; + importButton.style.borderColor = 'var(--nl-primary-color)'; + importButton.style.color = 'var(--nl-primary-color)'; + importButton.style.cursor = 'pointer'; } else { formatHint.textContent = '❌ Invalid key format - must be nsec1... or 64-character hex'; formatHint.style.color = '#dc2626'; + // Disable button + importButton.disabled = true; + importButton.style.borderColor = 'var(--nl-muted-color)'; + importButton.style.color = 'var(--nl-muted-color)'; + importButton.style.cursor = 'not-allowed'; } }; - const importButton = document.createElement('button'); - importButton.textContent = 'Import Key'; - importButton.onclick = () => this._importLocalKey(textarea.value); - importButton.style.cssText = this._getButtonStyle(); - const backButton = document.createElement('button'); backButton.textContent = 'Back'; - backButton.onclick = () => this._showLocalKeyScreen(); + backButton.onclick = () => this._renderLoginOptions(); backButton.style.cssText = this._getButtonStyle('secondary') + 'margin-top: 12px;'; - this.modalBody.appendChild(title); this.modalBody.appendChild(description); this.modalBody.appendChild(textarea); this.modalBody.appendChild(formatHint); this.modalBody.appendChild(importButton); this.modalBody.appendChild(backButton); + + // Add click handler for the "generate new" link + const generateLink = document.getElementById('generate-new'); + if (generateLink) { + generateLink.addEventListener('mouseenter', () => { + generateLink.style.color = 'var(--nl-accent-color)'; + }); + generateLink.addEventListener('mouseleave', () => { + generateLink.style.color = 'var(--nl-primary-color)'; + }); + generateLink.addEventListener('click', () => { + this._generateNewLocalKey(textarea, formatHint); + }); + } } + _generateNewLocalKey(textarea, formatHint) { + try { + // Generate a new secret key using NostrTools + const sk = window.NostrTools.generateSecretKey(); + const nsec = window.NostrTools.nip19.nsecEncode(sk); + + // Set the generated key in the textarea + textarea.value = nsec; + + // Trigger the oninput event to properly validate and enable the button + if (textarea.oninput) { + textarea.oninput(); + } + + console.log('Generated new local secret key (nsec format)'); + + } catch (error) { + console.error('Failed to generate local key:', error); + formatHint.textContent = '❌ Failed to generate key - NostrTools not available'; + formatHint.style.color = '#dc2626'; + } + } + + _createLocalKey() { + try { + const sk = window.NostrTools.generateSecretKey(); + const pk = window.NostrTools.getPublicKey(sk); + const nsec = window.NostrTools.nip19.nsecEncode(sk); + const npub = window.NostrTools.nip19.npubEncode(pk); + + this._showKeyDisplay(pk, nsec, 'created'); + } catch (error) { + this._showError('Failed to create key: ' + error.message); + } + } + + + _detectKeyFormat(keyValue) { const trimmed = keyValue.trim(); diff --git a/lite/ui/modal.js b/lite/ui/modal.js index 7046838..56eb21b 100644 --- a/lite/ui/modal.js +++ b/lite/ui/modal.js @@ -700,62 +700,8 @@ class Modal { _showLocalKeyScreen() { this.modalBody.innerHTML = ''; - const title = document.createElement('h3'); - title.textContent = 'Local Key'; - title.style.cssText = 'margin: 0 0 20px 0; font-size: 18px; font-weight: 600;'; - - const createButton = document.createElement('button'); - createButton.textContent = 'Create New Key'; - createButton.onclick = () => this._createLocalKey(); - createButton.style.cssText = this._getButtonStyle(); - - const importButton = document.createElement('button'); - importButton.textContent = 'Import Existing Key'; - importButton.onclick = () => this._showImportKeyForm(); - importButton.style.cssText = this._getButtonStyle() + 'margin-top: 12px;'; - - const backButton = document.createElement('button'); - backButton.textContent = 'Back'; - backButton.onclick = () => this._renderLoginOptions(); - backButton.style.cssText = ` - display: block; - margin-top: 20px; - padding: 12px; - background: #6b7280; - color: white; - border: none; - border-radius: 6px; - cursor: pointer; - `; - - this.modalBody.appendChild(title); - this.modalBody.appendChild(createButton); - this.modalBody.appendChild(importButton); - this.modalBody.appendChild(backButton); - } - - _createLocalKey() { - try { - const sk = window.NostrTools.generateSecretKey(); - const pk = window.NostrTools.getPublicKey(sk); - const nsec = window.NostrTools.nip19.nsecEncode(sk); - const npub = window.NostrTools.nip19.npubEncode(pk); - - this._showKeyDisplay(pk, nsec, 'created'); - } catch (error) { - this._showError('Failed to create key: ' + error.message); - } - } - - _showImportKeyForm() { - this.modalBody.innerHTML = ''; - - const title = document.createElement('h3'); - title.textContent = 'Import Local Key'; - title.style.cssText = 'margin: 0 0 16px 0; font-size: 18px; font-weight: 600;'; - const description = document.createElement('p'); - description.textContent = 'Enter your secret key in either nsec or hex format:'; + description.innerHTML = 'Enter your secret key in nsec or hex format, or generate new.'; description.style.cssText = 'margin-bottom: 12px; color: #6b7280; font-size: 14px;'; const textarea = document.createElement('textarea'); @@ -777,10 +723,40 @@ class Modal { const formatHint = document.createElement('div'); formatHint.style.cssText = 'margin-bottom: 16px; font-size: 12px; color: #6b7280; min-height: 16px;'; + const importButton = document.createElement('button'); + importButton.textContent = 'Import Key'; + importButton.disabled = true; + importButton.onclick = () => { + if (!importButton.disabled) { + this._importLocalKey(textarea.value); + } + }; + + // Set initial disabled state + importButton.style.cssText = ` + display: block; + width: 100%; + padding: 12px; + border: var(--nl-border-width) solid var(--nl-muted-color); + border-radius: var(--nl-border-radius); + font-size: 16px; + font-weight: 500; + cursor: not-allowed; + transition: all 0.2s; + font-family: var(--nl-font-family, 'Courier New', monospace); + background: var(--nl-secondary-color); + color: var(--nl-muted-color); + `; + textarea.oninput = () => { const value = textarea.value.trim(); if (!value) { formatHint.textContent = ''; + // Disable button + importButton.disabled = true; + importButton.style.borderColor = 'var(--nl-muted-color)'; + importButton.style.color = 'var(--nl-muted-color)'; + importButton.style.cursor = 'not-allowed'; return; } @@ -788,33 +764,94 @@ class Modal { if (format === 'nsec') { formatHint.textContent = '✅ Valid nsec format detected'; formatHint.style.color = '#059669'; + // Enable button + importButton.disabled = false; + importButton.style.borderColor = 'var(--nl-primary-color)'; + importButton.style.color = 'var(--nl-primary-color)'; + importButton.style.cursor = 'pointer'; } else if (format === 'hex') { formatHint.textContent = '✅ Valid hex format detected'; formatHint.style.color = '#059669'; + // Enable button + importButton.disabled = false; + importButton.style.borderColor = 'var(--nl-primary-color)'; + importButton.style.color = 'var(--nl-primary-color)'; + importButton.style.cursor = 'pointer'; } else { formatHint.textContent = '❌ Invalid key format - must be nsec1... or 64-character hex'; formatHint.style.color = '#dc2626'; + // Disable button + importButton.disabled = true; + importButton.style.borderColor = 'var(--nl-muted-color)'; + importButton.style.color = 'var(--nl-muted-color)'; + importButton.style.cursor = 'not-allowed'; } }; - const importButton = document.createElement('button'); - importButton.textContent = 'Import Key'; - importButton.onclick = () => this._importLocalKey(textarea.value); - importButton.style.cssText = this._getButtonStyle(); - const backButton = document.createElement('button'); backButton.textContent = 'Back'; - backButton.onclick = () => this._showLocalKeyScreen(); + backButton.onclick = () => this._renderLoginOptions(); backButton.style.cssText = this._getButtonStyle('secondary') + 'margin-top: 12px;'; - this.modalBody.appendChild(title); this.modalBody.appendChild(description); this.modalBody.appendChild(textarea); this.modalBody.appendChild(formatHint); this.modalBody.appendChild(importButton); this.modalBody.appendChild(backButton); + + // Add click handler for the "generate new" link + const generateLink = document.getElementById('generate-new'); + if (generateLink) { + generateLink.addEventListener('mouseenter', () => { + generateLink.style.color = 'var(--nl-accent-color)'; + }); + generateLink.addEventListener('mouseleave', () => { + generateLink.style.color = 'var(--nl-primary-color)'; + }); + generateLink.addEventListener('click', () => { + this._generateNewLocalKey(textarea, formatHint); + }); + } } + _generateNewLocalKey(textarea, formatHint) { + try { + // Generate a new secret key using NostrTools + const sk = window.NostrTools.generateSecretKey(); + const nsec = window.NostrTools.nip19.nsecEncode(sk); + + // Set the generated key in the textarea + textarea.value = nsec; + + // Trigger the oninput event to properly validate and enable the button + if (textarea.oninput) { + textarea.oninput(); + } + + console.log('Generated new local secret key (nsec format)'); + + } catch (error) { + console.error('Failed to generate local key:', error); + formatHint.textContent = '❌ Failed to generate key - NostrTools not available'; + formatHint.style.color = '#dc2626'; + } + } + + _createLocalKey() { + try { + const sk = window.NostrTools.generateSecretKey(); + const pk = window.NostrTools.getPublicKey(sk); + const nsec = window.NostrTools.nip19.nsecEncode(sk); + const npub = window.NostrTools.nip19.npubEncode(pk); + + this._showKeyDisplay(pk, nsec, 'created'); + } catch (error) { + this._showError('Failed to create key: ' + error.message); + } + } + + + _detectKeyFormat(keyValue) { const trimmed = keyValue.trim();