Fully functioning theme system. Bugs fixed
This commit is contained in:
@@ -205,22 +205,44 @@ The following features are planned but not yet implemented:
|
|||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
To work on the source files:
|
⚠️ **CRITICAL: DO NOT EDIT `nostr-lite.js` DIRECTLY!**
|
||||||
|
|
||||||
|
The `nostr-lite.js` file is **auto-generated** by the build script. All changes must be made in the build script itself.
|
||||||
|
|
||||||
|
### Build Process
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Edit individual components
|
# The main library source code is in:
|
||||||
lite/core/nip46-client.js
|
lite/build.js # ← Edit this file for library changes
|
||||||
lite/ui/modal.js
|
|
||||||
lite/nostr-login-lite.js
|
|
||||||
|
|
||||||
# Run bundler to create distribution
|
# To make changes:
|
||||||
node lite/bundler.js
|
1. Edit lite/build.js # Contains all source code
|
||||||
|
2. cd lite && node build.js # Regenerates nostr-lite.js
|
||||||
|
3. Test your changes in examples/
|
||||||
|
|
||||||
# Start dev server (from project root)
|
# NEVER edit these files directly (they get overwritten):
|
||||||
|
lite/nostr-lite.js # ← Auto-generated, don't edit!
|
||||||
|
|
||||||
|
# Separate components that can be edited:
|
||||||
|
lite/ui/modal.js # Modal UI component
|
||||||
|
themes/default/theme.css # Default theme
|
||||||
|
themes/dark/theme.css # Dark theme
|
||||||
|
```
|
||||||
|
|
||||||
|
### Development Workflow
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Make changes to source
|
||||||
|
nano lite/build.js
|
||||||
|
|
||||||
|
# 2. Rebuild bundle
|
||||||
|
cd lite && node build.js
|
||||||
|
|
||||||
|
# 3. Start dev server (from project root)
|
||||||
python3 -m http.server 8000
|
python3 -m http.server 8000
|
||||||
|
|
||||||
# Open test page
|
# 4. Test changes
|
||||||
open http://localhost:8000/examples/simple-demo.html
|
open http://localhost:8000/examples/modal.html
|
||||||
```
|
```
|
||||||
|
|
||||||
### Local Bundle Setup
|
### Local Bundle Setup
|
||||||
|
|||||||
1069
lite/build.js
1069
lite/build.js
File diff suppressed because it is too large
Load Diff
2130
lite/nostr-lite.js
2130
lite/nostr-lite.js
File diff suppressed because it is too large
Load Diff
276
lite/ui/modal.js
276
lite/ui/modal.js
@@ -4,11 +4,13 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
class Modal {
|
class Modal {
|
||||||
constructor(options) {
|
constructor(options = {}) {
|
||||||
this.options = options;
|
this.options = options;
|
||||||
this.container = null;
|
this.container = null;
|
||||||
this.isVisible = false;
|
this.isVisible = false;
|
||||||
this.currentScreen = null;
|
this.currentScreen = null;
|
||||||
|
this.isEmbedded = !!options.embedded;
|
||||||
|
this.embeddedContainer = options.embedded;
|
||||||
|
|
||||||
// Initialize modal container and styles
|
// Initialize modal container and styles
|
||||||
this._initModal();
|
this._initModal();
|
||||||
@@ -17,32 +19,59 @@ class Modal {
|
|||||||
_initModal() {
|
_initModal() {
|
||||||
// Create modal container
|
// Create modal container
|
||||||
this.container = document.createElement('div');
|
this.container = document.createElement('div');
|
||||||
this.container.id = 'nl-modal';
|
this.container.id = this.isEmbedded ? 'nl-modal-embedded' : 'nl-modal';
|
||||||
this.container.style.cssText = `
|
|
||||||
position: fixed;
|
if (this.isEmbedded) {
|
||||||
top: 0;
|
// Embedded mode: inline positioning, no overlay
|
||||||
left: 0;
|
this.container.style.cssText = `
|
||||||
right: 0;
|
position: relative;
|
||||||
bottom: 0;
|
display: none;
|
||||||
background: rgba(0, 0, 0, 0.75);
|
font-family: var(--nl-font-family, 'Courier New', monospace);
|
||||||
display: none;
|
width: 100%;
|
||||||
z-index: 10000;
|
`;
|
||||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
} else {
|
||||||
`;
|
// Modal mode: fixed overlay
|
||||||
|
this.container.style.cssText = `
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: rgba(0, 0, 0, 0.75);
|
||||||
|
display: none;
|
||||||
|
z-index: 10000;
|
||||||
|
font-family: var(--nl-font-family, 'Courier New', monospace);
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
// Create modal content
|
// Create modal content
|
||||||
const modalContent = document.createElement('div');
|
const modalContent = document.createElement('div');
|
||||||
modalContent.style.cssText = `
|
if (this.isEmbedded) {
|
||||||
position: relative;
|
// Embedded content: no centering margin, full width
|
||||||
background: white;
|
modalContent.style.cssText = `
|
||||||
width: 90%;
|
position: relative;
|
||||||
max-width: 400px;
|
background: var(--nl-secondary-color);
|
||||||
margin: 50px auto;
|
color: var(--nl-primary-color);
|
||||||
border-radius: 12px;
|
width: 100%;
|
||||||
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
|
border-radius: var(--nl-border-radius, 15px);
|
||||||
max-height: 600px;
|
border: var(--nl-border-width) solid var(--nl-primary-color);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
`;
|
`;
|
||||||
|
} else {
|
||||||
|
// Modal content: centered with margin
|
||||||
|
modalContent.style.cssText = `
|
||||||
|
position: relative;
|
||||||
|
background: var(--nl-secondary-color);
|
||||||
|
color: var(--nl-primary-color);
|
||||||
|
width: 90%;
|
||||||
|
max-width: 400px;
|
||||||
|
margin: 50px auto;
|
||||||
|
border-radius: var(--nl-border-radius, 15px);
|
||||||
|
border: var(--nl-border-width) solid var(--nl-primary-color);
|
||||||
|
max-height: 600px;
|
||||||
|
overflow: hidden;
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
// Header
|
// Header
|
||||||
const modalHeader = document.createElement('div');
|
const modalHeader = document.createElement('div');
|
||||||
@@ -51,6 +80,8 @@ class Modal {
|
|||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
background: transparent;
|
||||||
|
border-bottom: none;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const modalTitle = document.createElement('h2');
|
const modalTitle = document.createElement('h2');
|
||||||
@@ -59,31 +90,44 @@ class Modal {
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: #1f2937;
|
color: var(--nl-primary-color);
|
||||||
|
font-family: var(--nl-font-family, 'Courier New', monospace);
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const closeButton = document.createElement('button');
|
|
||||||
closeButton.innerHTML = '×';
|
|
||||||
closeButton.onclick = () => this.close();
|
|
||||||
closeButton.style.cssText = `
|
|
||||||
background: none;
|
|
||||||
border: none;
|
|
||||||
font-size: 28px;
|
|
||||||
color: #6b7280;
|
|
||||||
cursor: pointer;
|
|
||||||
padding: 0;
|
|
||||||
width: 32px;
|
|
||||||
height: 32px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
border-radius: 6px;
|
|
||||||
`;
|
|
||||||
closeButton.onmouseover = () => closeButton.style.background = '#f3f4f6';
|
|
||||||
closeButton.onmouseout = () => closeButton.style.background = 'none';
|
|
||||||
|
|
||||||
modalHeader.appendChild(modalTitle);
|
modalHeader.appendChild(modalTitle);
|
||||||
modalHeader.appendChild(closeButton);
|
|
||||||
|
// Only add close button for non-embedded modals
|
||||||
|
// Embedded modals shouldn't have a close button because there's no way to reopen them
|
||||||
|
if (!this.isEmbedded) {
|
||||||
|
const closeButton = document.createElement('button');
|
||||||
|
closeButton.innerHTML = '×';
|
||||||
|
closeButton.onclick = () => this.close();
|
||||||
|
closeButton.style.cssText = `
|
||||||
|
background: var(--nl-secondary-color);
|
||||||
|
border: var(--nl-border-width) solid var(--nl-primary-color);
|
||||||
|
border-radius: var(--nl-border-radius);
|
||||||
|
font-size: 28px;
|
||||||
|
color: var(--nl-primary-color);
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 0;
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-family: var(--nl-font-family, 'Courier New', monospace);
|
||||||
|
`;
|
||||||
|
closeButton.onmouseover = () => {
|
||||||
|
closeButton.style.borderColor = 'var(--nl-accent-color)';
|
||||||
|
closeButton.style.background = 'var(--nl-secondary-color)';
|
||||||
|
};
|
||||||
|
closeButton.onmouseout = () => {
|
||||||
|
closeButton.style.borderColor = 'var(--nl-primary-color)';
|
||||||
|
closeButton.style.background = 'var(--nl-secondary-color)';
|
||||||
|
};
|
||||||
|
|
||||||
|
modalHeader.appendChild(closeButton);
|
||||||
|
}
|
||||||
|
|
||||||
// Body
|
// Body
|
||||||
this.modalBody = document.createElement('div');
|
this.modalBody = document.createElement('div');
|
||||||
@@ -91,38 +135,52 @@ class Modal {
|
|||||||
padding: 24px;
|
padding: 24px;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
max-height: 500px;
|
max-height: 500px;
|
||||||
|
background: transparent;
|
||||||
|
font-family: var(--nl-font-family, 'Courier New', monospace);
|
||||||
`;
|
`;
|
||||||
|
|
||||||
modalContent.appendChild(modalHeader);
|
modalContent.appendChild(modalHeader);
|
||||||
modalContent.appendChild(this.modalBody);
|
modalContent.appendChild(this.modalBody);
|
||||||
this.container.appendChild(modalContent);
|
this.container.appendChild(modalContent);
|
||||||
|
|
||||||
// Add to body
|
// Add to appropriate parent
|
||||||
document.body.appendChild(this.container);
|
if (this.isEmbedded && this.embeddedContainer) {
|
||||||
|
// Append to specified container for embedding
|
||||||
// Click outside to close
|
if (typeof this.embeddedContainer === 'string') {
|
||||||
this.container.onclick = (e) => {
|
const targetElement = document.querySelector(this.embeddedContainer);
|
||||||
if (e.target === this.container) {
|
if (targetElement) {
|
||||||
this.close();
|
targetElement.appendChild(this.container);
|
||||||
|
} else {
|
||||||
|
console.error('NOSTR_LOGIN_LITE: Embedded container not found:', this.embeddedContainer);
|
||||||
|
document.body.appendChild(this.container);
|
||||||
|
}
|
||||||
|
} else if (this.embeddedContainer instanceof HTMLElement) {
|
||||||
|
this.embeddedContainer.appendChild(this.container);
|
||||||
|
} else {
|
||||||
|
console.error('NOSTR_LOGIN_LITE: Invalid embedded container');
|
||||||
|
document.body.appendChild(this.container);
|
||||||
}
|
}
|
||||||
};
|
} else {
|
||||||
|
// Add to body for modal mode
|
||||||
|
document.body.appendChild(this.container);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Click outside to close (only for modal mode)
|
||||||
|
if (!this.isEmbedded) {
|
||||||
|
this.container.onclick = (e) => {
|
||||||
|
if (e.target === this.container) {
|
||||||
|
this.close();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// Update theme
|
// Update theme
|
||||||
this.updateTheme();
|
this.updateTheme();
|
||||||
}
|
}
|
||||||
|
|
||||||
updateTheme() {
|
updateTheme() {
|
||||||
const isDark = this.options?.darkMode;
|
// The theme will automatically update through CSS custom properties
|
||||||
const modalContent = this.container.querySelector(':nth-child(1)');
|
// No manual styling needed - the CSS variables handle everything
|
||||||
const title = this.container.querySelector('h2');
|
|
||||||
|
|
||||||
if (isDark) {
|
|
||||||
modalContent.style.background = '#1f2937';
|
|
||||||
title.style.color = 'white';
|
|
||||||
} else {
|
|
||||||
modalContent.style.background = 'white';
|
|
||||||
title.style.color = '#1f2937';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
open(opts = {}) {
|
open(opts = {}) {
|
||||||
@@ -205,26 +263,41 @@ class Modal {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
margin-bottom: 12px;
|
margin-bottom: 12px;
|
||||||
background: ${this.options?.darkMode ? '#374151' : 'white'};
|
background: var(--nl-secondary-color);
|
||||||
border: 1px solid ${this.options?.darkMode ? '#4b5563' : '#d1d5db'};
|
color: var(--nl-primary-color);
|
||||||
border-radius: 8px;
|
border: var(--nl-border-width) solid var(--nl-primary-color);
|
||||||
|
border-radius: var(--nl-border-radius);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: all 0.2s;
|
transition: all 0.2s;
|
||||||
|
font-family: var(--nl-font-family, 'Courier New', monospace);
|
||||||
`;
|
`;
|
||||||
button.onmouseover = () => {
|
button.onmouseover = () => {
|
||||||
button.style.boxShadow = '0 4px 6px -1px rgba(0, 0, 0, 0.1)';
|
button.style.borderColor = 'var(--nl-accent-color)';
|
||||||
|
button.style.background = 'var(--nl-secondary-color)';
|
||||||
};
|
};
|
||||||
button.onmouseout = () => {
|
button.onmouseout = () => {
|
||||||
button.style.boxShadow = 'none';
|
button.style.borderColor = 'var(--nl-primary-color)';
|
||||||
|
button.style.background = 'var(--nl-secondary-color)';
|
||||||
};
|
};
|
||||||
|
|
||||||
const iconDiv = document.createElement('div');
|
const iconDiv = document.createElement('div');
|
||||||
iconDiv.textContent = option.icon;
|
// Replace emoji icons with text-based ones
|
||||||
|
const iconMap = {
|
||||||
|
'🔌': '[EXT]',
|
||||||
|
'🔑': '[KEY]',
|
||||||
|
'🌐': '[NET]',
|
||||||
|
'👁️': '[VIEW]',
|
||||||
|
'📱': '[SMS]'
|
||||||
|
};
|
||||||
|
iconDiv.textContent = iconMap[option.icon] || option.icon;
|
||||||
iconDiv.style.cssText = `
|
iconDiv.style.cssText = `
|
||||||
font-size: 24px;
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
margin-right: 16px;
|
margin-right: 16px;
|
||||||
width: 24px;
|
width: 50px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
color: var(--nl-primary-color);
|
||||||
|
font-family: var(--nl-font-family, 'Courier New', monospace);
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const contentDiv = document.createElement('div');
|
const contentDiv = document.createElement('div');
|
||||||
@@ -235,14 +308,16 @@ class Modal {
|
|||||||
titleDiv.style.cssText = `
|
titleDiv.style.cssText = `
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
margin-bottom: 4px;
|
margin-bottom: 4px;
|
||||||
color: ${this.options?.darkMode ? 'white' : '#1f2937'};
|
color: var(--nl-primary-color);
|
||||||
|
font-family: var(--nl-font-family, 'Courier New', monospace);
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const descDiv = document.createElement('div');
|
const descDiv = document.createElement('div');
|
||||||
descDiv.textContent = option.description;
|
descDiv.textContent = option.description;
|
||||||
descDiv.style.cssText = `
|
descDiv.style.cssText = `
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: ${this.options?.darkMode ? '#9ca3af' : '#6b7280'};
|
color: #666666;
|
||||||
|
font-family: var(--nl-font-family, 'Courier New', monospace);
|
||||||
`;
|
`;
|
||||||
|
|
||||||
contentDiv.appendChild(titleDiv);
|
contentDiv.appendChild(titleDiv);
|
||||||
@@ -446,11 +521,22 @@ class Modal {
|
|||||||
|
|
||||||
const title = document.createElement('h3');
|
const title = document.createElement('h3');
|
||||||
title.textContent = 'Choose Browser Extension';
|
title.textContent = 'Choose Browser Extension';
|
||||||
title.style.cssText = 'margin: 0 0 16px 0; font-size: 18px; font-weight: 600;';
|
title.style.cssText = `
|
||||||
|
margin: 0 0 16px 0;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--nl-primary-color);
|
||||||
|
font-family: var(--nl-font-family, 'Courier New', monospace);
|
||||||
|
`;
|
||||||
|
|
||||||
const description = document.createElement('p');
|
const description = document.createElement('p');
|
||||||
description.textContent = `Found ${extensions.length} Nostr extensions. Choose which one to use:`;
|
description.textContent = `Found ${extensions.length} Nostr extensions. Choose which one to use:`;
|
||||||
description.style.cssText = 'margin-bottom: 20px; color: #6b7280; font-size: 14px;';
|
description.style.cssText = `
|
||||||
|
margin-bottom: 20px;
|
||||||
|
color: #666666;
|
||||||
|
font-size: 14px;
|
||||||
|
font-family: var(--nl-font-family, 'Courier New', monospace);
|
||||||
|
`;
|
||||||
|
|
||||||
this.modalBody.appendChild(title);
|
this.modalBody.appendChild(title);
|
||||||
this.modalBody.appendChild(description);
|
this.modalBody.appendChild(description);
|
||||||
@@ -465,21 +551,23 @@ class Modal {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
margin-bottom: 12px;
|
margin-bottom: 12px;
|
||||||
background: ${this.options?.darkMode ? '#374151' : 'white'};
|
background: var(--nl-secondary-color);
|
||||||
border: 1px solid ${this.options?.darkMode ? '#4b5563' : '#d1d5db'};
|
color: var(--nl-primary-color);
|
||||||
border-radius: 8px;
|
border: var(--nl-border-width) solid var(--nl-primary-color);
|
||||||
|
border-radius: var(--nl-border-radius);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: all 0.2s;
|
transition: all 0.2s;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
|
font-family: var(--nl-font-family, 'Courier New', monospace);
|
||||||
`;
|
`;
|
||||||
|
|
||||||
button.onmouseover = () => {
|
button.onmouseover = () => {
|
||||||
button.style.boxShadow = '0 4px 6px -1px rgba(0, 0, 0, 0.1)';
|
button.style.borderColor = 'var(--nl-accent-color)';
|
||||||
button.style.transform = 'translateY(-1px)';
|
button.style.background = 'var(--nl-secondary-color)';
|
||||||
};
|
};
|
||||||
button.onmouseout = () => {
|
button.onmouseout = () => {
|
||||||
button.style.boxShadow = 'none';
|
button.style.borderColor = 'var(--nl-primary-color)';
|
||||||
button.style.transform = 'none';
|
button.style.background = 'var(--nl-secondary-color)';
|
||||||
};
|
};
|
||||||
|
|
||||||
const iconDiv = document.createElement('div');
|
const iconDiv = document.createElement('div');
|
||||||
@@ -499,15 +587,16 @@ class Modal {
|
|||||||
nameDiv.style.cssText = `
|
nameDiv.style.cssText = `
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
margin-bottom: 4px;
|
margin-bottom: 4px;
|
||||||
color: ${this.options?.darkMode ? 'white' : '#1f2937'};
|
color: var(--nl-primary-color);
|
||||||
|
font-family: var(--nl-font-family, 'Courier New', monospace);
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const pathDiv = document.createElement('div');
|
const pathDiv = document.createElement('div');
|
||||||
pathDiv.textContent = ext.name;
|
pathDiv.textContent = ext.name;
|
||||||
pathDiv.style.cssText = `
|
pathDiv.style.cssText = `
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
color: ${this.options?.darkMode ? '#9ca3af' : '#6b7280'};
|
color: #666666;
|
||||||
font-family: monospace;
|
font-family: var(--nl-font-family, 'Courier New', monospace);
|
||||||
`;
|
`;
|
||||||
|
|
||||||
contentDiv.appendChild(nameDiv);
|
contentDiv.appendChild(nameDiv);
|
||||||
@@ -1041,23 +1130,24 @@ class Modal {
|
|||||||
display: block;
|
display: block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
border: none;
|
border: var(--nl-border-width) solid var(--nl-primary-color);
|
||||||
border-radius: 8px;
|
border-radius: var(--nl-border-radius);
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: all 0.2s;
|
transition: all 0.2s;
|
||||||
|
font-family: var(--nl-font-family, 'Courier New', monospace);
|
||||||
`;
|
`;
|
||||||
|
|
||||||
if (type === 'primary') {
|
if (type === 'primary') {
|
||||||
return baseStyle + `
|
return baseStyle + `
|
||||||
background: #3b82f6;
|
background: var(--nl-secondary-color);
|
||||||
color: white;
|
color: var(--nl-primary-color);
|
||||||
`;
|
`;
|
||||||
} else {
|
} else {
|
||||||
return baseStyle + `
|
return baseStyle + `
|
||||||
background: #6b7280;
|
background: #cccccc;
|
||||||
color: white;
|
color: var(--nl-primary-color);
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user