button example
This commit is contained in:
@@ -1,50 +0,0 @@
|
||||
# NOSTR_LOGIN_LITE Examples
|
||||
|
||||
This directory contains examples and tests for NOSTR_LOGIN_LITE using the local bundle setup.
|
||||
|
||||
## Files
|
||||
|
||||
### 🔬 comprehensive-test.html
|
||||
**The main diagnostic and testing tool**
|
||||
- Comprehensive test suite with extensive debugging output
|
||||
- Tests all library functions, dependencies, crypto, storage, etc.
|
||||
- Results displayed on webpage for easy copying and debugging
|
||||
- Run this when you need to diagnose issues or verify functionality
|
||||
|
||||
### 📱 simple-demo.html
|
||||
Basic demonstration of NOSTR_LOGIN_LITE integration
|
||||
- Minimal setup example
|
||||
- Good starting point for new implementations
|
||||
|
||||
### 🎨 modal-login-demo.html
|
||||
Demonstrates modal-based login flow
|
||||
- Shows how to trigger and handle the login modal
|
||||
- Example of auth event handling
|
||||
|
||||
### 👤 login-and-profile.html
|
||||
Login and user profile demonstration
|
||||
- Shows authentication flow
|
||||
- Displays user profile information after login
|
||||
|
||||
### 🔗 nip46-bunker-demo.html
|
||||
NIP-46 remote signing demonstration
|
||||
- Shows how to connect to remote signers/bunkers
|
||||
- Advanced use case example
|
||||
|
||||
## Usage
|
||||
|
||||
1. Start a local web server (e.g., `python -m http.server 5501` or Live Server in VS Code)
|
||||
2. Navigate to any HTML file
|
||||
3. For comprehensive testing and debugging, use `comprehensive-test.html`
|
||||
|
||||
All examples use the local bundle setup with two files:
|
||||
1. `../lite/nostr.bundle.js` - Official nostr-tools bundle
|
||||
2. `../lite/nostr-lite.js` - NOSTR_LOGIN_LITE library with embedded NIP-46 extension
|
||||
|
||||
## Architecture Update (2025-09-13)
|
||||
|
||||
The library has been simplified from a three-file to a two-file architecture:
|
||||
- ✅ **Before:** `nostr.bundle.js` + `nip46-extension.js` + `nostr-lite.js`
|
||||
- ✅ **Now:** `nostr.bundle.js` + `nostr-lite.js` (with embedded NIP-46)
|
||||
|
||||
All functionality remains identical - NIP-46 remote signing, all auth methods, and full compatibility are preserved.
|
||||
75
examples/button.html
Normal file
75
examples/button.html
Normal file
@@ -0,0 +1,75 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Embedded NOSTR_LOGIN_LITE</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
margin: 0;
|
||||
padding: 40px;
|
||||
background: white;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 90vh;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 400px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#login-button {
|
||||
background: #0066cc;
|
||||
color: white;
|
||||
padding: 12px 24px;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
text-align: center;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
|
||||
#login-button:hover {
|
||||
background: #0052a3;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container">
|
||||
<div id="login-button">Login</div>
|
||||
</div>
|
||||
|
||||
<script src="../lite/nostr.bundle.js"></script>
|
||||
<script src="../lite/nostr-lite.js"></script>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', async () => {
|
||||
await window.NOSTR_LOGIN_LITE.init({
|
||||
theme: 'default',
|
||||
methods: {
|
||||
extension: true,
|
||||
local: true,
|
||||
readonly: true,
|
||||
connect: true,
|
||||
remote: true,
|
||||
otp: true
|
||||
},
|
||||
floatingTab: {
|
||||
enabled: false
|
||||
}
|
||||
});
|
||||
|
||||
document.getElementById('login-button').addEventListener('click', () => {
|
||||
window.NOSTR_LOGIN_LITE.launch('login');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -37,6 +37,7 @@
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', async () => {
|
||||
await window.NOSTR_LOGIN_LITE.init({
|
||||
theme:'default',
|
||||
methods: {
|
||||
extension: true,
|
||||
local: true,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
@@ -9,249 +10,33 @@
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
margin: 0;
|
||||
padding: 20px;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
background: #ffffff;
|
||||
min-height: 100vh;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1000px;
|
||||
margin: 0 auto;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 20px;
|
||||
padding: 30px;
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
background: linear-gradient(45deg, #fff, #007bff);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
}
|
||||
|
||||
.status {
|
||||
padding: 15px;
|
||||
border-radius: 10px;
|
||||
margin: 15px 0;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.status.logged-out {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
.status.logged-in {
|
||||
background: rgba(76, 175, 80, 0.2);
|
||||
border: 1px solid rgba(76, 175, 80, 0.5);
|
||||
}
|
||||
|
||||
.status.loading {
|
||||
background: rgba(255, 193, 7, 0.2);
|
||||
border: 1px solid rgba(255, 193, 7, 0.5);
|
||||
}
|
||||
|
||||
.button {
|
||||
background: linear-gradient(45deg, #007bff, #0056b3);
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 15px 30px;
|
||||
border-radius: 25px;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
display: inline-block;
|
||||
margin: 10px 5px;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.button:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 15px rgba(0, 123, 255, 0.4);
|
||||
}
|
||||
|
||||
.button.secondary {
|
||||
background: linear-gradient(45deg, #6c757d, #495057);
|
||||
}
|
||||
|
||||
.button.secondary:hover {
|
||||
box-shadow: 0 4px 15px rgba(108, 117, 125, 0.4);
|
||||
}
|
||||
|
||||
.button.danger {
|
||||
background: linear-gradient(45deg, #dc3545, #c82333);
|
||||
}
|
||||
|
||||
.button.danger:hover {
|
||||
box-shadow: 0 4px 15px rgba(220, 53, 69, 0.4);
|
||||
}
|
||||
|
||||
.profile-card {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 15px;
|
||||
padding: 20px;
|
||||
margin: 20px 0;
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.profile-header {
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.profile-avatar {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
border-radius: 50%;
|
||||
margin: 0 auto 15px;
|
||||
display: block;
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.profile-name {
|
||||
font-size: 24px;
|
||||
font-weight: 700;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.profile-about {
|
||||
font-style: italic;
|
||||
opacity: 0.8;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.profile-pubkey {
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: 14px;
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
padding: 10px;
|
||||
border-radius: 5px;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.console-output {
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
border-radius: 10px;
|
||||
padding: 20px;
|
||||
margin: 20px 0;
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.console-entry {
|
||||
margin: 5px 0;
|
||||
}
|
||||
|
||||
.console-timestamp {
|
||||
color: #ccc;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.info-section {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 10px;
|
||||
padding: 20px;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.info-section h3 {
|
||||
margin-top: 0;
|
||||
color: #007bff;
|
||||
}
|
||||
|
||||
.methods-list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.methods-list li {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
margin: 10px 0;
|
||||
padding: 10px 15px;
|
||||
border-radius: 8px;
|
||||
border-left: 4px solid #007bff;
|
||||
color: #000000;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>🔐 NOSTR_LOGIN_LITE - All Login Methods Test</h1>
|
||||
|
||||
<div id="status" class="status logged-out">
|
||||
⏳ Initializing NOSTR_LOGIN_LITE...
|
||||
</div>
|
||||
|
||||
<div id="login-section">
|
||||
<button id="launch-modal" class="button">🚀 Launch Login Modal</button>
|
||||
<p style="font-size: 14px; opacity: 0.8; margin-top: 10px;">
|
||||
Click to open the NOSTR_LOGIN_LITE modal and test all available login methods:
|
||||
</p>
|
||||
|
||||
<div class="info-section">
|
||||
<h3>Available Login Methods</h3>
|
||||
<ul class="methods-list">
|
||||
<li><strong>Browser Extension:</strong> Alby, nos2x, etc. (if installed)</li>
|
||||
<li><strong>Local Key:</strong> Generate new keys or import existing private key/nsec</li>
|
||||
<li><strong>Read Only:</strong> Access public content without authentication</li>
|
||||
<li><strong>Nostr Connect (NIP-46):</strong> Connect to remote signing services</li>
|
||||
<li><strong>DM/OTP:</strong> Secure local accounts with one-time passwords</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="profile-section" style="display: none;">
|
||||
<div class="profile-card">
|
||||
<div class="profile-header">
|
||||
<img id="profile-picture" class="profile-avatar" src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iODAiIGhlaWdodD0iODAiIHZpZXdCb3g9IjAgMCA4MCA4MCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPGNpcmNsZSBjeD0iNDAiIGN5PSI0MCIgcj0iNDAiIGZpbGw9IiNmNmY2ZjYiLz4KPHBhdGggZD0iTTQwIDQ1QzQ1IDQ1IDUwIDQyIDUwIDQwUzQ1IDQxIDQwIDQ1WiIgc3Ryb2tlPSIjYmJkIiBzdHJva2Utd2lkdGg9IjIiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPgo8Y2lyY2xlIGN4PSIzNSIgY3k9IjM1IiByPSIyIiBmaWxsPSIjYmJkIi8+CjxjaXJjbGUgY3g9IjQ1IiBjeT0iMzUiIHI9IjIiIGZpbGw9IiNiYmQiLz4KPGRlZnM+Cj08L2RlZnM+Cjwvc3ZnPgo=" alt="Profile Picture">
|
||||
<div id="profile-name" class="profile-name">Loading...</div>
|
||||
<div id="profile-about" class="profile-about"></div>
|
||||
</div>
|
||||
<div>
|
||||
<strong>Public Key:</strong>
|
||||
<div id="profile-pubkey" class="profile-pubkey"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button id="refresh-profile" class="button secondary">🔄 Refresh Profile</button>
|
||||
<button id="logout" class="button danger">🚪 Logout</button>
|
||||
</div>
|
||||
|
||||
<div class="console-output" id="console-display">
|
||||
<div class="console-entry">
|
||||
<span class="console-timestamp">[Console]</span> Ready for testing
|
||||
</div>
|
||||
</div>
|
||||
<div id="login-section">
|
||||
<!-- Login UI if needed -->
|
||||
</div>
|
||||
<div id="profile-section">
|
||||
<img id="profile-picture">
|
||||
<div id="profile-pubkey"></div>
|
||||
<div id="profile-name"></div>
|
||||
<div id="profile-about"></div>
|
||||
</div>
|
||||
|
||||
<!-- Load the official nostr-tools bundle first -->
|
||||
<script src="../lite/nostr.bundle.js"></script>
|
||||
|
||||
|
||||
<!-- Load NOSTR_LOGIN_LITE main library (now includes NIP-46 extension) -->
|
||||
<script src="../lite/nostr-lite.js"></script>
|
||||
|
||||
<script>
|
||||
// Console logging helper
|
||||
function log(level, message) {
|
||||
const consoleDiv = document.getElementById('console-display');
|
||||
const entry = document.createElement('div');
|
||||
entry.className = 'console-entry';
|
||||
|
||||
const timestamp = new Date().toLocaleTimeString();
|
||||
const prefix = level === 'ERROR' ? '[ERROR]' :
|
||||
level === 'SUCCESS' ? '[SUCCESS]' :
|
||||
level === 'WARNING' ? '[WARNING]' : '[INFO]';
|
||||
|
||||
entry.innerHTML = `<span class="console-timestamp">[${timestamp}] ${prefix}</span> ${message}`;
|
||||
consoleDiv.appendChild(entry);
|
||||
consoleDiv.scrollTop = consoleDiv.scrollHeight;
|
||||
}
|
||||
|
||||
// Global variables
|
||||
let nlLite = null;
|
||||
@@ -260,11 +45,11 @@
|
||||
|
||||
// Initialize NOSTR_LOGIN_LITE
|
||||
async function initializeApp() {
|
||||
log('INFO', 'Initializing NOSTR_LOGIN_LITE...');
|
||||
// console.log('INFO', 'Initializing NOSTR_LOGIN_LITE...');
|
||||
|
||||
try {
|
||||
await window.NOSTR_LOGIN_LITE.init({
|
||||
theme: 'dark',
|
||||
theme: 'default',
|
||||
darkMode: false,
|
||||
relays: [relayUrl, 'wss://relay.damus.io'],
|
||||
methods: {
|
||||
@@ -300,137 +85,67 @@
|
||||
});
|
||||
|
||||
nlLite = window.NOSTR_LOGIN_LITE;
|
||||
log('SUCCESS', 'NOSTR_LOGIN_LITE initialized successfully');
|
||||
document.getElementById('status').innerHTML = '✅ Ready - Click "Launch Login Modal" to test all login methods';
|
||||
document.getElementById('status').className = 'status logged-in';
|
||||
console.log('SUCCESS', 'NOSTR_LOGIN_LITE initialized successfully');
|
||||
|
||||
window.addEventListener('nlMethodSelected', handleAuthEvent);
|
||||
|
||||
} catch (error) {
|
||||
log('ERROR', `Initialization failed: ${error.message}`);
|
||||
document.getElementById('status').innerHTML = '❌ Failed to initialize NOSTR_LOGIN_LITE';
|
||||
document.getElementById('status').className = 'status logged-out';
|
||||
console.log('ERROR', `Initialization failed: ${error.message}`);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Launch the login modal
|
||||
async function launchLoginModal() {
|
||||
log('INFO', 'Launching NOSTR_LOGIN_LITE modal...');
|
||||
document.getElementById('status').innerHTML = '🔄 Opening login modal...';
|
||||
document.getElementById('status').className = 'status loading';
|
||||
|
||||
try {
|
||||
// Launch the modal
|
||||
await nlLite.launch('login');
|
||||
log('SUCCESS', 'Login modal launched successfully');
|
||||
|
||||
} catch (error) {
|
||||
log('ERROR', `Failed to launch modal: ${error.message}`);
|
||||
document.getElementById('status').innerHTML = '❌ Failed to launch modal';
|
||||
document.getElementById('status').className = 'status logged-out';
|
||||
}
|
||||
}
|
||||
|
||||
// Handle authentication events
|
||||
function handleAuthEvent(event) {
|
||||
const { type, pubkey, method, error } = event.detail;
|
||||
|
||||
log('INFO', `Auth event received: type=${type}, method=${method}`);
|
||||
|
||||
if (type === 'login' && pubkey) {
|
||||
const {pubkey, method, error } = event.detail;
|
||||
console.log('INFO', `Auth event received: method=${method}`);
|
||||
|
||||
if (method && pubkey) {
|
||||
userPubkey = pubkey;
|
||||
log('SUCCESS', `Login successful! Method: ${method}, Pubkey: ${pubkey}`);
|
||||
|
||||
document.getElementById('status').innerHTML = `✅ Logged in via ${method}!`;
|
||||
document.getElementById('status').className = 'status logged-in';
|
||||
|
||||
// Show profile section
|
||||
document.getElementById('login-section').style.display = 'none';
|
||||
document.getElementById('profile-section').style.display = 'block';
|
||||
|
||||
// Load profile
|
||||
console.log('SUCCESS', `Login successful! Method: ${method}, Pubkey: ${pubkey}`);
|
||||
|
||||
loadUserProfile();
|
||||
|
||||
} else if (type === 'logout') {
|
||||
log('INFO', 'User logged out');
|
||||
userPubkey = null;
|
||||
|
||||
document.getElementById('status').innerHTML = '✅ Ready - Click "Launch Login Modal" to test all login methods';
|
||||
document.getElementById('status').className = 'status logged-in';
|
||||
|
||||
// Show login section
|
||||
document.getElementById('login-section').style.display = 'block';
|
||||
document.getElementById('profile-section').style.display = 'none';
|
||||
|
||||
|
||||
} else if (error) {
|
||||
log('ERROR', `Authentication error: ${error}`);
|
||||
document.getElementById('status').innerHTML = '❌ Authentication failed';
|
||||
document.getElementById('status').className = 'status logged-out';
|
||||
console.log('ERROR', `Authentication error: ${error}`);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Load user profile
|
||||
// Load user profile using nostr-tools pool
|
||||
async function loadUserProfile() {
|
||||
if (!userPubkey) return;
|
||||
|
||||
log('INFO', `Loading profile for: ${userPubkey}`);
|
||||
console.log('INFO', `Loading profile for: ${userPubkey}`);
|
||||
document.getElementById('profile-name').textContent = 'Loading profile...';
|
||||
document.getElementById('profile-pubkey').textContent = userPubkey;
|
||||
|
||||
try {
|
||||
// Simple WebSocket connection to get profile
|
||||
const ws = new WebSocket(relayUrl);
|
||||
// Create a SimplePool instance
|
||||
const pool = new window.NostrTools.SimplePool();
|
||||
const relays = [relayUrl, 'wss://relay.laantungir.net'];
|
||||
|
||||
// Get profile event (kind 0) for the user using querySync
|
||||
const events = await pool.querySync(relays, {
|
||||
kinds: [0],
|
||||
authors: [userPubkey],
|
||||
limit: 1
|
||||
});
|
||||
|
||||
ws.onopen = () => {
|
||||
log('SUCCESS', 'WebSocket connected, requesting profile...');
|
||||
const req = JSON.stringify([
|
||||
'REQ',
|
||||
'profile',
|
||||
{
|
||||
kinds: [0],
|
||||
authors: [userPubkey],
|
||||
limit: 1
|
||||
}
|
||||
]);
|
||||
ws.send(req);
|
||||
};
|
||||
pool.close(relays); // Clean up connections
|
||||
|
||||
ws.onmessage = (event) => {
|
||||
try {
|
||||
const message = JSON.parse(event.data);
|
||||
const [type, subscriptionId, eventData] = message;
|
||||
|
||||
if (type === 'EVENT' && eventData && eventData.kind === 0) {
|
||||
log('SUCCESS', 'Profile event received');
|
||||
const profile = JSON.parse(eventData.content);
|
||||
displayProfile(profile);
|
||||
ws.close();
|
||||
} else if (type === 'EOSE') {
|
||||
log('INFO', 'End of subscription');
|
||||
ws.close();
|
||||
}
|
||||
} catch (parseError) {
|
||||
log('ERROR', `Failed to parse WebSocket message: ${parseError.message}`);
|
||||
}
|
||||
};
|
||||
|
||||
ws.onerror = (error) => {
|
||||
log('ERROR', `WebSocket error: ${error.message || 'Connection failed'}`);
|
||||
document.getElementById('profile-name').textContent = 'Error loading profile';
|
||||
};
|
||||
|
||||
// Timeout after 5 seconds
|
||||
setTimeout(() => {
|
||||
if (ws.readyState !== WebSocket.CLOSED) {
|
||||
ws.close();
|
||||
if (document.getElementById('profile-name').textContent === 'Loading profile...') {
|
||||
document.getElementById('profile-name').textContent = 'Profile timeout';
|
||||
document.getElementById('profile-about').textContent = 'Could not load profile from relay.';
|
||||
log('WARNING', 'Profile request timed out');
|
||||
}
|
||||
}
|
||||
}, 5000);
|
||||
if (events.length > 0) {
|
||||
console.log('SUCCESS', 'Profile event received');
|
||||
const profile = JSON.parse(events[0].content);
|
||||
displayProfile(profile);
|
||||
} else {
|
||||
console.log('INFO', 'No profile found');
|
||||
document.getElementById('profile-name').textContent = 'No profile found';
|
||||
document.getElementById('profile-about').textContent = 'User has not set up a profile yet.';
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
log('ERROR', `Profile loading failed: ${error.message}`);
|
||||
console.log('ERROR', `Profile loading failed: ${error.message}`);
|
||||
document.getElementById('profile-name').textContent = 'Error loading profile';
|
||||
document.getElementById('profile-about').textContent = error.message;
|
||||
}
|
||||
@@ -449,33 +164,26 @@
|
||||
document.getElementById('profile-picture').src = picture;
|
||||
}
|
||||
|
||||
log('SUCCESS', `Profile displayed: ${name}`);
|
||||
console.log('SUCCESS', `Profile displayed: ${name}`);
|
||||
}
|
||||
|
||||
// Logout function
|
||||
async function logout() {
|
||||
log('INFO', 'Logging out...');
|
||||
console.log('INFO', 'Logging out...');
|
||||
try {
|
||||
await nlLite.logout();
|
||||
log('SUCCESS', 'Logged out successfully');
|
||||
console.log('SUCCESS', 'Logged out successfully');
|
||||
} catch (error) {
|
||||
log('ERROR', `Logout failed: ${error.message}`);
|
||||
console.log('ERROR', `Logout failed: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Set up event listeners
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// Button event listeners
|
||||
document.getElementById('launch-modal').addEventListener('click', launchLoginModal);
|
||||
document.getElementById('refresh-profile').addEventListener('click', loadUserProfile);
|
||||
document.getElementById('logout').addEventListener('click', logout);
|
||||
|
||||
// Listen for authentication events
|
||||
window.addEventListener('nlAuth', handleAuthEvent);
|
||||
|
||||
// Initialize the app
|
||||
setTimeout(initializeApp, 100);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -1,411 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>🔐 NOSTR_LOGIN_LITE - Full Modal Login Demo</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
margin: 0;
|
||||
padding: 20px;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
min-height: 100vh;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 20px;
|
||||
padding: 30px;
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
background: linear-gradient(45deg, #fff, #007bff);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
}
|
||||
|
||||
.demo-section {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 15px;
|
||||
padding: 20px;
|
||||
margin: 20px 0;
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.demo-section h2 {
|
||||
margin-top: 0;
|
||||
font-size: 24px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.button {
|
||||
background: linear-gradient(45deg, #007bff, #0056b3);
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 15px 30px;
|
||||
border-radius: 25px;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
display: inline-block;
|
||||
margin: 10px 5px;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.button:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 15px rgba(0, 123, 255, 0.4);
|
||||
}
|
||||
|
||||
.button.secondary {
|
||||
background: linear-gradient(45deg, #6c757d, #495057);
|
||||
}
|
||||
|
||||
.button.secondary:hover {
|
||||
box-shadow: 0 4px 15px rgba(108, 117, 125, 0.4);
|
||||
}
|
||||
|
||||
.status {
|
||||
display: inline-block;
|
||||
padding: 8px 12px;
|
||||
border-radius: 4px;
|
||||
margin: 10px 0;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.status.success { background: rgba(76, 175, 80, 0.2); color: #81c784; }
|
||||
.status.error { background: rgba(244, 67, 54, 0.2); color: #ef5350; }
|
||||
.status.warning { background: rgba(255, 193, 7, 0.2); color: #ffd54f; }
|
||||
.status.info { background: rgba(33, 150, 243, 0.2); color: #64b5f6; }
|
||||
|
||||
.console-output {
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
border-radius: 10px;
|
||||
padding: 20px;
|
||||
margin: 20px 0;
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.console-entry {
|
||||
margin: 5px 0;
|
||||
}
|
||||
|
||||
.console-timestamp {
|
||||
color: #ccc;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.feature-list {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
gap: 15px;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.feature-item {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
padding: 15px;
|
||||
border-radius: 10px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.feature-item .icon {
|
||||
font-size: 24px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.feature-item h3 {
|
||||
margin: 0 0 5px 0;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.feature-item p {
|
||||
margin: 0;
|
||||
opacity: 0.8;
|
||||
font-size: 14px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>🔐 NOSTR_LOGIN_LITE Full Modal Login Demo</h1>
|
||||
|
||||
<div class="demo-section">
|
||||
<h2>📚 Available Login Methods</h2>
|
||||
<p>This demo showcases all login methods provided by NOSTR_LOGIN_LITE:</p>
|
||||
|
||||
<div class="feature-list">
|
||||
<div class="feature-item">
|
||||
<div class="icon">📱</div>
|
||||
<h3>Extension Login</h3>
|
||||
<p>Use browser extensions like Alby, nos2x, or other Nostr-compatible extensions</p>
|
||||
</div>
|
||||
<div class="feature-item">
|
||||
<div class="icon">💾</div>
|
||||
<h3>Local Account</h3>
|
||||
<p>Create and manage local Nostr keypairs stored in browser storage</p>
|
||||
</div>
|
||||
<div class="feature-item">
|
||||
<div class="icon">👁️</div>
|
||||
<h3>Read-Only Account</h3>
|
||||
<p>Access public content without authentication (limited functionality)</p>
|
||||
</div>
|
||||
<div class="feature-item">
|
||||
<div class="icon">🔗</div>
|
||||
<h3>NIP-46 Remote</h3>
|
||||
<p>Connect to remote signers for secure key management</p>
|
||||
</div>
|
||||
<div class="feature-item">
|
||||
<div class="icon">🔐</div>
|
||||
<h3>OTP Backup</h3>
|
||||
<p>Secure local accounts with time-based one-time passwords</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Library Status -->
|
||||
<div class="demo-section">
|
||||
<h2>⚙️ Library Status</h2>
|
||||
<div id="dep-status" class="status info">Loading nostr-tools...</div>
|
||||
<div id="lib-status" class="status info">Loading NOSTR_LOGIN_LITE...</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal Authentication -->
|
||||
<div class="demo-section">
|
||||
<h2>🎯 Launch Full Login Modal</h2>
|
||||
<p>Click the button below to launch the complete authentication modal with all available login options:</p>
|
||||
<button id="launch-auth" class="button">🚀 Launch Authentication Modal</button>
|
||||
<button onclick="location.reload()" class="button secondary">🔄 Reload Page</button>
|
||||
<div id="auth-status" class="status" style="margin-top: 15px;">Ready to authenticate...</div>
|
||||
<div style="font-size: 14px; opacity: 0.8; margin-top: 10px;">
|
||||
The modal will show all available login methods based on your browser setup and library configuration.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- User Info Display (shown after login) -->
|
||||
<div id="user-info" class="demo-section" style="display: none;">
|
||||
<h2>👤 User Information</h2>
|
||||
<div id="user-details">
|
||||
<strong>Public Key:</strong> <span id="user-pubkey">Loading...</span><br>
|
||||
<strong>Login Method:</strong> <span id="user-method">Loading...</span><br>
|
||||
<strong>Account Type:</strong> <span id="user-type">Loading...</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Console Log -->
|
||||
<div class="console-output" id="console-display">
|
||||
<div class="console-entry">
|
||||
<span class="console-timestamp">[Demo]</span> Modal Login Demo initialized
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Load the official nostr-tools bundle first -->
|
||||
<script src="../lite/nostr.bundle.js"></script>
|
||||
|
||||
<!-- Load NOSTR_LOGIN_LITE main library (now includes NIP-46 extension) -->
|
||||
<script src="../lite/nostr-lite.js"></script>
|
||||
|
||||
<script>
|
||||
// Console logging helper
|
||||
function addConsoleEntry(message, type = 'info') {
|
||||
const consoleDiv = document.getElementById('console-display');
|
||||
const entry = document.createElement('div');
|
||||
entry.className = 'console-entry';
|
||||
|
||||
const timestamp = new Date().toLocaleTimeString();
|
||||
const prefix = type === 'error' ? '[ERROR]' :
|
||||
type === 'success' ? '[SUCCESS]' :
|
||||
type === 'warning' ? '[WARNING]' : '[INFO]';
|
||||
|
||||
entry.innerHTML = `<span class="console-timestamp">[${timestamp}] ${prefix}</span> ${message}`;
|
||||
consoleDiv.appendChild(entry);
|
||||
consoleDiv.scrollTop = consoleDiv.scrollHeight;
|
||||
}
|
||||
|
||||
// Global state
|
||||
let authInitialized = false;
|
||||
|
||||
// Event listeners for authentication events
|
||||
window.addEventListener('nlAuth', (event) => {
|
||||
addConsoleEntry(`Authentication event: ${event.detail.type}`, 'success');
|
||||
if (event.detail.pubkey) {
|
||||
addConsoleEntry(`User authenticated: ${event.detail.pubkey}`, 'success');
|
||||
displayUserInfo(event.detail);
|
||||
}
|
||||
});
|
||||
|
||||
window.addEventListener('nlLogout', (event) => {
|
||||
addConsoleEntry('User logged out', 'warning');
|
||||
hideUserInfo();
|
||||
});
|
||||
|
||||
window.addEventListener('nlAuthUrl', (event) => {
|
||||
addConsoleEntry(`Auth URL generated: ${event.detail.url}`, 'info');
|
||||
});
|
||||
|
||||
window.addEventListener('nlError', (event) => {
|
||||
addConsoleEntry(`Authentication error: ${event.detail.message}`, 'error');
|
||||
});
|
||||
|
||||
// Library load checking with retry
|
||||
function checkLibraryLoaded() {
|
||||
let attempts = 0;
|
||||
const maxAttempts = 50; // 5 seconds
|
||||
|
||||
const check = () => {
|
||||
if (window.NostrTools) {
|
||||
document.getElementById('dep-status').textContent = '✓ nostr-tools loaded successfully!';
|
||||
document.getElementById('dep-status').className = 'status success';
|
||||
}
|
||||
|
||||
if (window.NOSTR_LOGIN_LITE) {
|
||||
document.getElementById('lib-status').textContent = '✓ NOSTR_LOGIN_LITE loaded successfully!';
|
||||
document.getElementById('lib-status').className = 'status success';
|
||||
enableModalLaunch();
|
||||
} else {
|
||||
attempts++;
|
||||
if (attempts < maxAttempts) {
|
||||
setTimeout(check, 100);
|
||||
} else {
|
||||
document.getElementById('lib-status').textContent = '✗ Failed to load NOSTR_LOGIN_LITE';
|
||||
document.getElementById('lib-status').className = 'status error';
|
||||
addConsoleEntry('Bundle might have JavaScript errors - check browser console', 'error');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
check();
|
||||
}
|
||||
|
||||
// Enable the modal launch button
|
||||
function enableModalLaunch() {
|
||||
const launchBtn = document.getElementById('launch-auth');
|
||||
launchBtn.disabled = false;
|
||||
launchBtn.textContent = '🚀 Launch Authentication Modal';
|
||||
addConsoleEntry('Full modal authentication ready', 'success');
|
||||
}
|
||||
|
||||
// Launch authentication modal
|
||||
async function launchAuthModal() {
|
||||
const launchBtn = document.getElementById('launch-auth');
|
||||
const status = document.getElementById('auth-status');
|
||||
|
||||
try {
|
||||
status.textContent = '🔄 Initializing authentication...';
|
||||
status.className = 'status warning';
|
||||
launchBtn.disabled = true;
|
||||
|
||||
// Initialize NOSTR_LOGIN_LITE with all methods enabled
|
||||
const options = {
|
||||
theme: 'dark',
|
||||
darkMode: false,
|
||||
relays: ['wss://relay.damus.io', 'wss://relay.nostr.band', 'wss://nos.lol'],
|
||||
methods: {
|
||||
extension: true,
|
||||
local: true,
|
||||
readonly: true,
|
||||
remote: true,
|
||||
otp: true
|
||||
},
|
||||
debug: true
|
||||
};
|
||||
|
||||
addConsoleEntry('Initializing NOSTR_LOGIN_LITE with full configuration', 'info');
|
||||
|
||||
if (!authInitialized) {
|
||||
await window.NOSTR_LOGIN_LITE.init(options);
|
||||
authInitialized = true;
|
||||
}
|
||||
|
||||
addConsoleEntry('Launching full authentication modal', 'info');
|
||||
status.textContent = '🎨 Opening authentication modal...';
|
||||
|
||||
// Launch the modal - this will show all available methods
|
||||
window.NOSTR_LOGIN_LITE.launch('login');
|
||||
|
||||
status.textContent = '✅ Authentication modal launched!';
|
||||
status.className = 'status success';
|
||||
|
||||
addConsoleEntry('Modal launched successfully - all login methods available', 'success');
|
||||
|
||||
// Re-enable button after a delay
|
||||
setTimeout(() => {
|
||||
launchBtn.disabled = false;
|
||||
launchBtn.textContent = '🔄 Launch Again';
|
||||
status.textContent = 'Ready to launch modal again...';
|
||||
status.className = 'status info';
|
||||
}, 3000);
|
||||
|
||||
} catch (error) {
|
||||
addConsoleEntry(`Modal launch failed: ${error.message}`, 'error');
|
||||
status.textContent = '❌ Failed to launch modal';
|
||||
status.className = 'status error';
|
||||
launchBtn.disabled = false;
|
||||
launchBtn.textContent = '🚀 Try Again';
|
||||
}
|
||||
}
|
||||
|
||||
// Display user information after successful authentication
|
||||
function displayUserInfo(details) {
|
||||
document.getElementById('user-info').style.display = 'block';
|
||||
document.getElementById('user-pubkey').textContent = details.pubkey || 'Unknown';
|
||||
document.getElementById('user-method').textContent = details.method || 'Unknown';
|
||||
document.getElementById('user-type').textContent = getAccountType(details.method);
|
||||
|
||||
const status = document.getElementById('auth-status');
|
||||
status.textContent = '✅ Successfully authenticated!';
|
||||
status.className = 'status success';
|
||||
}
|
||||
|
||||
// Hide user info on logout
|
||||
function hideUserInfo() {
|
||||
document.getElementById('user-info').style.display = 'none';
|
||||
|
||||
const status = document.getElementById('auth-status');
|
||||
status.textContent = '👋 User logged out';
|
||||
status.className = 'status warning';
|
||||
}
|
||||
|
||||
// Helper function to get readable account type
|
||||
function getAccountType(method) {
|
||||
const types = {
|
||||
extension: 'Browser Extension',
|
||||
local: 'Local Account',
|
||||
readonly: 'Read-Only Account',
|
||||
remote: 'NIP-46 Remote',
|
||||
otp: 'OTP Secured Local'
|
||||
};
|
||||
return types[method] || 'Unknown';
|
||||
}
|
||||
|
||||
// Initialize everything when DOM loads
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
addConsoleEntry('Demo page loaded, initializing libraries...', 'info');
|
||||
|
||||
// Check if libraries are loaded
|
||||
checkLibraryLoaded();
|
||||
|
||||
// Set up the modal launch button
|
||||
document.getElementById('launch-auth').addEventListener('click', launchAuthModal);
|
||||
});
|
||||
|
||||
// Debug logging
|
||||
console.log('NOSTR_LOGIN_LITE Modal Demo loaded');
|
||||
console.log('Available login methods will be shown in modal');
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
75
examples/modal.html
Normal file
75
examples/modal.html
Normal file
@@ -0,0 +1,75 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Embedded NOSTR_LOGIN_LITE</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
margin: 0;
|
||||
padding: 40px;
|
||||
background: white;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 90vh;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 400px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#login-container {
|
||||
/* No styling - let embedded modal blend seamlessly */
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
|
||||
<script src="../lite/nostr.bundle.js"></script>
|
||||
<script src="../lite/nostr-lite.js"></script>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', async () => {
|
||||
await window.NOSTR_LOGIN_LITE.init({
|
||||
theme: 'dark',
|
||||
methods: {
|
||||
extension: true,
|
||||
local: true,
|
||||
readonly: true,
|
||||
connect: true,
|
||||
remote: true,
|
||||
otp: true
|
||||
},
|
||||
floatingTab: {
|
||||
enabled: true,
|
||||
hPosition: 0.7, // 0.0-1.0 or '95%' from left
|
||||
vPosition: 0.5, // 0.0-1.0 or '50%' from top
|
||||
appearance: {
|
||||
style: 'pill', // 'pill', 'square', 'circle', 'minimal'
|
||||
theme: 'auto', // 'auto' follows main theme
|
||||
icon: '[LOGIN]', // Now uses text-based icons like [LOGIN], [KEY], [NET]
|
||||
text: 'Login'
|
||||
},
|
||||
behavior: {
|
||||
hideWhenAuthenticated: false,
|
||||
showUserInfo: true,
|
||||
autoSlide: true
|
||||
},
|
||||
animation: {
|
||||
slideDirection: 'auto' // 'auto', 'left', 'right', 'up', 'down'
|
||||
}
|
||||
|
||||
}});
|
||||
|
||||
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
Reference in New Issue
Block a user