Fully functioning theme system. Bugs fixed

This commit is contained in:
Your Name
2025-09-14 13:23:52 -04:00
parent 3b1eb7f951
commit b59bf17372
4 changed files with 2124 additions and 1393 deletions

View File

@@ -205,22 +205,44 @@ The following features are planned but not yet implemented:
## 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
# Edit individual components
lite/core/nip46-client.js
lite/ui/modal.js
lite/nostr-login-lite.js
# The main library source code is in:
lite/build.js # ← Edit this file for library changes
# Run bundler to create distribution
node lite/bundler.js
# To make changes:
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
# Open test page
open http://localhost:8000/examples/simple-demo.html
# 4. Test changes
open http://localhost:8000/examples/modal.html
```
### Local Bundle Setup

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -4,11 +4,13 @@
*/
class Modal {
constructor(options) {
constructor(options = {}) {
this.options = options;
this.container = null;
this.isVisible = false;
this.currentScreen = null;
this.isEmbedded = !!options.embedded;
this.embeddedContainer = options.embedded;
// Initialize modal container and styles
this._initModal();
@@ -17,32 +19,59 @@ class Modal {
_initModal() {
// Create modal container
this.container = document.createElement('div');
this.container.id = 'nl-modal';
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: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
`;
this.container.id = this.isEmbedded ? 'nl-modal-embedded' : 'nl-modal';
if (this.isEmbedded) {
// Embedded mode: inline positioning, no overlay
this.container.style.cssText = `
position: relative;
display: none;
font-family: var(--nl-font-family, 'Courier New', monospace);
width: 100%;
`;
} 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
const modalContent = document.createElement('div');
modalContent.style.cssText = `
position: relative;
background: white;
width: 90%;
max-width: 400px;
margin: 50px auto;
border-radius: 12px;
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
max-height: 600px;
overflow: hidden;
`;
if (this.isEmbedded) {
// Embedded content: no centering margin, full width
modalContent.style.cssText = `
position: relative;
background: var(--nl-secondary-color);
color: var(--nl-primary-color);
width: 100%;
border-radius: var(--nl-border-radius, 15px);
border: var(--nl-border-width) solid var(--nl-primary-color);
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
const modalHeader = document.createElement('div');
@@ -51,6 +80,8 @@ class Modal {
display: flex;
justify-content: space-between;
align-items: center;
background: transparent;
border-bottom: none;
`;
const modalTitle = document.createElement('h2');
@@ -59,31 +90,44 @@ class Modal {
margin: 0;
font-size: 24px;
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(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
this.modalBody = document.createElement('div');
@@ -91,38 +135,52 @@ class Modal {
padding: 24px;
overflow-y: auto;
max-height: 500px;
background: transparent;
font-family: var(--nl-font-family, 'Courier New', monospace);
`;
modalContent.appendChild(modalHeader);
modalContent.appendChild(this.modalBody);
this.container.appendChild(modalContent);
// Add to body
document.body.appendChild(this.container);
// Click outside to close
this.container.onclick = (e) => {
if (e.target === this.container) {
this.close();
// Add to appropriate parent
if (this.isEmbedded && this.embeddedContainer) {
// Append to specified container for embedding
if (typeof this.embeddedContainer === 'string') {
const targetElement = document.querySelector(this.embeddedContainer);
if (targetElement) {
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
this.updateTheme();
}
updateTheme() {
const isDark = this.options?.darkMode;
const modalContent = this.container.querySelector(':nth-child(1)');
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';
}
// The theme will automatically update through CSS custom properties
// No manual styling needed - the CSS variables handle everything
}
open(opts = {}) {
@@ -205,26 +263,41 @@ class Modal {
width: 100%;
padding: 16px;
margin-bottom: 12px;
background: ${this.options?.darkMode ? '#374151' : 'white'};
border: 1px solid ${this.options?.darkMode ? '#4b5563' : '#d1d5db'};
border-radius: 8px;
background: var(--nl-secondary-color);
color: var(--nl-primary-color);
border: var(--nl-border-width) solid var(--nl-primary-color);
border-radius: var(--nl-border-radius);
cursor: pointer;
transition: all 0.2s;
font-family: var(--nl-font-family, 'Courier New', monospace);
`;
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.style.boxShadow = 'none';
button.style.borderColor = 'var(--nl-primary-color)';
button.style.background = 'var(--nl-secondary-color)';
};
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 = `
font-size: 24px;
font-size: 16px;
font-weight: bold;
margin-right: 16px;
width: 24px;
width: 50px;
text-align: center;
color: var(--nl-primary-color);
font-family: var(--nl-font-family, 'Courier New', monospace);
`;
const contentDiv = document.createElement('div');
@@ -235,14 +308,16 @@ class Modal {
titleDiv.style.cssText = `
font-weight: 600;
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');
descDiv.textContent = option.description;
descDiv.style.cssText = `
font-size: 14px;
color: ${this.options?.darkMode ? '#9ca3af' : '#6b7280'};
color: #666666;
font-family: var(--nl-font-family, 'Courier New', monospace);
`;
contentDiv.appendChild(titleDiv);
@@ -446,11 +521,22 @@ class Modal {
const title = document.createElement('h3');
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');
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(description);
@@ -465,21 +551,23 @@ class Modal {
width: 100%;
padding: 16px;
margin-bottom: 12px;
background: ${this.options?.darkMode ? '#374151' : 'white'};
border: 1px solid ${this.options?.darkMode ? '#4b5563' : '#d1d5db'};
border-radius: 8px;
background: var(--nl-secondary-color);
color: var(--nl-primary-color);
border: var(--nl-border-width) solid var(--nl-primary-color);
border-radius: var(--nl-border-radius);
cursor: pointer;
transition: all 0.2s;
text-align: left;
font-family: var(--nl-font-family, 'Courier New', monospace);
`;
button.onmouseover = () => {
button.style.boxShadow = '0 4px 6px -1px rgba(0, 0, 0, 0.1)';
button.style.transform = 'translateY(-1px)';
button.style.borderColor = 'var(--nl-accent-color)';
button.style.background = 'var(--nl-secondary-color)';
};
button.onmouseout = () => {
button.style.boxShadow = 'none';
button.style.transform = 'none';
button.style.borderColor = 'var(--nl-primary-color)';
button.style.background = 'var(--nl-secondary-color)';
};
const iconDiv = document.createElement('div');
@@ -499,15 +587,16 @@ class Modal {
nameDiv.style.cssText = `
font-weight: 600;
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');
pathDiv.textContent = ext.name;
pathDiv.style.cssText = `
font-size: 12px;
color: ${this.options?.darkMode ? '#9ca3af' : '#6b7280'};
font-family: monospace;
color: #666666;
font-family: var(--nl-font-family, 'Courier New', monospace);
`;
contentDiv.appendChild(nameDiv);
@@ -1041,23 +1130,24 @@ class Modal {
display: block;
width: 100%;
padding: 12px;
border: none;
border-radius: 8px;
border: var(--nl-border-width) solid var(--nl-primary-color);
border-radius: var(--nl-border-radius);
font-size: 16px;
font-weight: 500;
cursor: pointer;
transition: all 0.2s;
font-family: var(--nl-font-family, 'Courier New', monospace);
`;
if (type === 'primary') {
return baseStyle + `
background: #3b82f6;
color: white;
background: var(--nl-secondary-color);
color: var(--nl-primary-color);
`;
} else {
return baseStyle + `
background: #6b7280;
color: white;
background: #cccccc;
color: var(--nl-primary-color);
`;
}
}