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();