Still don't have dm working because I can't decrypt at primal.

This commit is contained in:
Your Name
2025-09-13 14:36:21 -04:00
parent bad361a686
commit 72b0d9b102
144 changed files with 5808 additions and 26105 deletions

50
examples/README.md Normal file
View File

@@ -0,0 +1,50 @@
# 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.

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,459 @@
<!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 - All Login Methods Test</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: 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;
}
</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>
<!-- 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;
let userPubkey = null;
let relayUrl = 'wss://relay.laantungir.net';
// Initialize NOSTR_LOGIN_LITE
async function initializeApp() {
log('INFO', 'Initializing NOSTR_LOGIN_LITE...');
try {
await window.NOSTR_LOGIN_LITE.init({
theme: 'dark',
darkMode: false,
relays: [relayUrl, 'wss://relay.damus.io'],
methods: {
extension: true,
local: true,
readonly: true,
remote: true, // Enables "Nostr Connect" (NIP-46)
otp: true // Enables "DM/OTP"
},
debug: true
});
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';
} 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';
}
}
// 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) {
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
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';
}
}
// Load user profile
async function loadUserProfile() {
if (!userPubkey) return;
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);
ws.onopen = () => {
log('SUCCESS', 'WebSocket connected, requesting profile...');
const req = JSON.stringify([
'REQ',
'profile',
{
kinds: [0],
authors: [userPubkey],
limit: 1
}
]);
ws.send(req);
};
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);
} catch (error) {
log('ERROR', `Profile loading failed: ${error.message}`);
document.getElementById('profile-name').textContent = 'Error loading profile';
document.getElementById('profile-about').textContent = error.message;
}
}
// Display profile data
function displayProfile(profile) {
const name = profile.name || profile.display_name || profile.displayName || 'Anonymous User';
const about = profile.about || '';
const picture = profile.picture || '';
document.getElementById('profile-name').textContent = name;
document.getElementById('profile-about').textContent = about;
if (picture) {
document.getElementById('profile-picture').src = picture;
}
log('SUCCESS', `Profile displayed: ${name}`);
}
// Logout function
async function logout() {
log('INFO', 'Logging out...');
try {
await nlLite.logout();
log('SUCCESS', 'Logged out successfully');
} catch (error) {
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>

View File

@@ -1,121 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Minimal Nostr Login Test (Remote)</title>
</head>
<body>
<div id="user-info" style="display: none;">
<div id="user-name"></div>
<img id="user-picture" style="display: none;">
</div>
<script>
// Load nostr-login script with configuration from your website
const script = document.createElement('script');
script.src = 'https://laantungir.net/nostr-login/unpkg.js';
// Configure with Laan theme and Welcome Login start screen
script.setAttribute('data-theme', 'laan');
script.setAttribute('data-start-screen', 'welcome-login');
script.setAttribute('data-methods', 'connect,extension,local,readOnly');
script.setAttribute('data-no-banner', 'false');
document.head.appendChild(script);
// Wait for script to load, then set up event listeners
script.onload = () => {
setupEventListeners();
};
function setupEventListeners() {
// Listen for authentication events
document.addEventListener('nlAuth', (e) => {
const { type, pubkey, name } = e.detail;
if (type === 'login' || type === 'signup') {
console.log('User logged in:', e.detail);
showUserInfo();
// Check for profile updates periodically
startProfilePolling();
} else if (type === 'logout') {
console.log('User logged out');
hideUserInfo();
stopProfilePolling();
}
});
}
let profileInterval;
function startProfilePolling() {
// Check immediately
updateUserProfile();
// Then check every 2 seconds for profile updates
profileInterval = setInterval(updateUserProfile, 2000);
// Stop checking after 15 seconds (profile should be loaded by then)
setTimeout(stopProfilePolling, 15000);
}
function stopProfilePolling() {
if (profileInterval) {
clearInterval(profileInterval);
profileInterval = null;
}
}
function updateUserProfile() {
try {
// Try to get user info from localStorage (current user)
const userInfo = JSON.parse(localStorage.getItem('__nostrlogin_nip46') || 'null');
if (userInfo) {
updateUserDisplay(userInfo);
}
} catch (error) {
// Silently handle errors to avoid interference
}
}
function showUserInfo() {
const userInfoDiv = document.getElementById('user-info');
userInfoDiv.style.display = 'block';
updateUserProfile();
}
function updateUserDisplay(userInfo) {
const userNameDiv = document.getElementById('user-name');
const userPicture = document.getElementById('user-picture');
// Display name with proper fallback hierarchy
const displayName = userInfo.name ||
(userInfo.nip05 && userInfo.nip05.split('@')[0]) ||
(userInfo.pubkey && userInfo.pubkey.substring(0, 16) + '...') ||
'Unknown';
userNameDiv.textContent = displayName;
// Update profile picture if available
if (userInfo.picture) {
userPicture.src = userInfo.picture;
userPicture.style.display = 'block';
userPicture.style.width = '200px';
// userPicture.style.height = '200px';
}
}
function hideUserInfo() {
const userInfoDiv = document.getElementById('user-info');
const userNameDiv = document.getElementById('user-name');
const userPicture = document.getElementById('user-picture');
userInfoDiv.style.display = 'none';
userNameDiv.textContent = '';
userPicture.src = '';
userPicture.style.display = 'none';
}
</script>
</body>
</html>

View File

@@ -1,121 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Minimal Nostr Login Test</title>
</head>
<body>
<div id="user-info" style="display: none;">
<div id="user-name"></div>
<img id="user-picture" style="display: none;">
</div>
<script>
// Load nostr-login script with configuration
const script = document.createElement('script');
script.src = '../packages/auth/dist/unpkg.js';
// Configure with Laan theme and Welcome Login start screen
script.setAttribute('data-theme', 'laan');
script.setAttribute('data-start-screen', 'welcome-login');
script.setAttribute('data-methods', 'connect,extension,local,readOnly');
script.setAttribute('data-no-banner', 'false');
document.head.appendChild(script);
// Wait for script to load, then set up event listeners
script.onload = () => {
setupEventListeners();
};
function setupEventListeners() {
// Listen for authentication events
document.addEventListener('nlAuth', (e) => {
const { type, pubkey, name } = e.detail;
if (type === 'login' || type === 'signup') {
console.log('User logged in:', e.detail);
showUserInfo();
// Check for profile updates periodically
startProfilePolling();
} else if (type === 'logout') {
console.log('User logged out');
hideUserInfo();
stopProfilePolling();
}
});
}
let profileInterval;
function startProfilePolling() {
// Check immediately
updateUserProfile();
// Then check every 2 seconds for profile updates
profileInterval = setInterval(updateUserProfile, 2000);
// Stop checking after 15 seconds (profile should be loaded by then)
setTimeout(stopProfilePolling, 15000);
}
function stopProfilePolling() {
if (profileInterval) {
clearInterval(profileInterval);
profileInterval = null;
}
}
function updateUserProfile() {
try {
// Try to get user info from localStorage (current user)
const userInfo = JSON.parse(localStorage.getItem('__nostrlogin_nip46') || 'null');
if (userInfo) {
updateUserDisplay(userInfo);
}
} catch (error) {
// Silently handle errors to avoid interference
}
}
function showUserInfo() {
const userInfoDiv = document.getElementById('user-info');
userInfoDiv.style.display = 'block';
updateUserProfile();
}
function updateUserDisplay(userInfo) {
const userNameDiv = document.getElementById('user-name');
const userPicture = document.getElementById('user-picture');
// Display name with proper fallback hierarchy
const displayName = userInfo.name ||
(userInfo.nip05 && userInfo.nip05.split('@')[0]) ||
(userInfo.pubkey && userInfo.pubkey.substring(0, 16) + '...') ||
'Unknown';
userNameDiv.textContent = displayName;
// Update profile picture if available
if (userInfo.picture) {
userPicture.src = userInfo.picture;
userPicture.style.display = 'block';
userPicture.style.width = '200px';
// userPicture.style.height = '200px';
}
}
function hideUserInfo() {
const userInfoDiv = document.getElementById('user-info');
const userNameDiv = document.getElementById('user-name');
const userPicture = document.getElementById('user-picture');
userInfoDiv.style.display = 'none';
userNameDiv.textContent = '';
userPicture.src = '';
userPicture.style.display = 'none';
}
</script>
</body>
</html>

View File

@@ -0,0 +1,411 @@
<!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>

View File

@@ -1,838 +0,0 @@
<!doctype html>
<html lang="" class="has-dark-text" dir="rtl">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>من الجميل في نوستر هي قدرة…</title>
<link
rel="stylesheet"
href="https://blossom.npubpro.com/9d9f5663fcc8ec87f256d3959fcd763ae234774d7cfcaf85a5ad10e3adea63e3.css"
/>
<style>
:root {
--background-color: #ffffff;
}
</style>
<script>
/* The script for calculating the color contrast was taken from
https://gomakethings.com/dynamically-changing-the-text-color-based-on-background-color-contrast-with-vanilla-js/ */
var accentColor = getComputedStyle(
document.documentElement,
).getPropertyValue('--background-color')
accentColor = accentColor.trim().slice(1)
var r = parseInt(accentColor.substr(0, 2), 16)
var g = parseInt(accentColor.substr(2, 2), 16)
var b = parseInt(accentColor.substr(4, 2), 16)
var yiq = (r * 299 + g * 587 + b * 114) / 1000
var textColor = yiq >= 128 ? 'dark' : 'light'
document.documentElement.className = `has-${textColor}-text`
</script>
<meta property="og:title" content="من الجميل في نوستر هي قدرة…" />
<meta name="twitter:title" content="من الجميل في نوستر هي قدرة…" />
<meta
name="description"
content="من الجميل في نوستر هي قدرة التطبيقات على التخاطر.
إذا بتفضلوا استخدام مساحة مثل الDiscord، ضيفوا صفحتنا على flotilla.social للتواصل بصيغة التشات.
اضغط إضافة مساحة Add Space،
ثم الدخول على مساحة Join a space،
وضيفوا ريلاي relay.nostrarabia.com
نراكم. 🫡
#nostrarabia…"
/>
<meta
property="og:description"
content="من الجميل في نوستر هي قدرة التطبيقات على التخاطر.
إذا بتفضلوا استخدام مساحة مثل الDiscord، ضيفوا صفحتنا على flotilla.social للتواصل بصيغة التشات.
اضغط إضافة مساحة Add Space،
ثم الدخول على مساحة Join a space،
وضيفوا ريلاي relay.nostrarabia.com
نراكم. 🫡
#nostrarabia…"
/>
<meta
name="twitter:description"
content="من الجميل في نوستر هي قدرة التطبيقات على التخاطر.
إذا بتفضلوا استخدام مساحة مثل الDiscord، ضيفوا صفحتنا على flotilla.social للتواصل بصيغة التشات.
اضغط إضافة مساحة Add Space،
ثم الدخول على مساحة Join a space،
وضيفوا ريلاي relay.nostrarabia.com
نراكم. 🫡
#nostrarabia…"
/>
<meta
property="og:image"
content="https://image.nostr.build/4afea744d3f72f4f7af1f7d632a8594159ef65f49b48cb00890c3904c57e1822.jpg"
/>
<meta
name="twitter:image"
content="https://image.nostr.build/4afea744d3f72f4f7af1f7d632a8594159ef65f49b48cb00890c3904c57e1822.jpg"
/>
<meta name="twitter:image:alt" content="من الجميل في نوستر هي قدرة…" />
<link
rel="canonical"
href="https://nostrarabia.npub.pro/post/note1dplcmt2885l9tsy6c4xenm3w7hrg92af42sz76p0ypd7ft0muvpqg80sqn/"
/>
<link
rel="og:url"
href="https://nostrarabia.npub.pro/post/note1dplcmt2885l9tsy6c4xenm3w7hrg92af42sz76p0ypd7ft0muvpqg80sqn/"
/>
<meta property="og:site_name" content="نوستر" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@nostrprotocol" />
<meta property="og:type" content="website" />
<!--
***********************
Powered by npub.pro
***********************
-->
<meta
name="nostr:site"
content="naddr1qqxkummnw3shywnzxg6kyv34qythwumn8ghj7un9d3shjtnwwp6kyurjduhxxmmdqgsgzt8ltta9m8pmgzptstrsxew06tf8nsn64yfrzwu0e07kt3q2a6crqsqqqaesldfhew"
/>
<meta name="author" content="نوستر بالعربي" />
<meta
name="nostr:author"
content="npub1syk07kh6tkwrksyzhqk8qdjul5kj08p842gjxyacljlavhzq4m4slmdu3p"
/>
<meta
name="nostr:id"
content="note1dplcmt2885l9tsy6c4xenm3w7hrg92af42sz76p0ypd7ft0muvpqg80sqn"
/>
<meta
name="nostr:event_id"
content="note1dplcmt2885l9tsy6c4xenm3w7hrg92af42sz76p0ypd7ft0muvpqg80sqn"
/>
<link
rel="manifest"
href="https://nostrarabia.npub.pro/manifest.webmanifest"
/>
<script
src="https://code.jquery.com/jquery-3.5.1.min.js"
integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0="
crossorigin="anonymous"
></script>
<link
rel="preload"
as="style"
href="https://cdn.jsdelivr.net/npm/venobox@2.1.8/dist/venobox.min.css"
/>
<link
rel="icon"
href="https://image.nostr.build/4afea744d3f72f4f7af1f7d632a8594159ef65f49b48cb00890c3904c57e1822.jpg"
type="image/jpeg"
/>
<link
rel="apple-touch-icon"
href="https://image.nostr.build/4afea744d3f72f4f7af1f7d632a8594159ef65f49b48cb00890c3904c57e1822.jpg"
/>
<meta name="theme-color" content="#ffffff" />
<script>
document.documentElement.setAttribute('dir', 'rtl')
</script>
<style>
.np-oembed-video-link {
display: inline-block;
position: relative;
}
.np-oembed-video-link img {
max-width: 100%;
}
.np-oembed-video-link:after {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 96px;
height: 96px;
background: url('data:image/png;base64,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');
background-size: cover;
content: '';
}
</style>
<style>
:root {
--ghost-accent-color: #139ad4;
}
</style>
<link rel="stylesheet" href="https://cdn.npubpro.com/maptalks.css" />
<link
rel="alternate"
type="application/rss+xml"
title="نوستر"
href="https://nostrarabia.npub.pro/rss/"
/>
<link
rel="stylesheet"
type="text/css"
href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@300..800&amp;display=swap"
/>
<link
rel="stylesheet"
type="text/css"
href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@300..800&amp;display=swap"
/>
<link
rel="preload"
as="script"
href="https://blossom.npubpro.com/64f7c3c5de348a7d1a5c7d1519abfa33fec8c5442c583fda441d25cd7b5990cf.js"
/>
<link
rel="preload"
as="style"
href="https://blossom.npubpro.com/8c397e35e20c7e4c6e163f0d17511294a2fba1c1a13823b91e2c220c3c38e83b.css"
/>
<link
rel="preload"
as="style"
href="https://blossom.npubpro.com/9d9f5663fcc8ec87f256d3959fcd763ae234774d7cfcaf85a5ad10e3adea63e3.css"
/>
<link
rel="preload"
as="script"
href="https://blossom.npubpro.com/b8292724b60caed6133f097c3f0427163e93e87724da84861192e1322d4146f5.js"
/>
<link
rel="preload"
as="script"
href="https://blossom.npubpro.com/b866f2b384af7668032c0c2fd523717520537bf812de9870cf1a960b11087333.js"
/>
<link
rel="preload"
as="style"
href="https://blossom.npubpro.com/eb267981d379bed63595b7ebd1dd6cb775c912a6f9b0f16c2faee5050afbf381.css"
/>
</head>
<body
class="post-template tag-nostrarabia is-head-left-logo has-parallax-feed is-dropdown-loaded"
style="overflow: initial"
>
<div class="gh-site">
<header id="gh-head" class="gh-head gh-outer">
<div class="gh-head-inner gh-inner">
<div class="gh-head-brand">
<div class="gh-head-brand-wrapper">
<a
class="gh-head-logo"
href="https://nostrarabia.npub.pro"
>
<img
src="https://image.nostr.build/4afea744d3f72f4f7af1f7d632a8594159ef65f49b48cb00890c3904c57e1822.jpg"
alt="نوستر"
/>
</a>
</div>
<button
class="gh-search gh-icon-btn"
aria-label="Search this site"
data-ghost-search=""
>
<svg
width="20"
height="20"
viewBox="0 0 20 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M17.5 17.5L12.5 12.5L17.5 17.5ZM14.1667 8.33333C14.1667 9.09938 14.0158 9.85792 13.7226 10.5657C13.4295 11.2734 12.9998 11.9164 12.4581 12.4581C11.9164 12.9998 11.2734 13.4295 10.5657 13.7226C9.85792 14.0158 9.09938 14.1667 8.33333 14.1667C7.56729 14.1667 6.80875 14.0158 6.10101 13.7226C5.39328 13.4295 4.75022 12.9998 4.20854 12.4581C3.66687 11.9164 3.23719 11.2734 2.94404 10.5657C2.65088 9.85792 2.5 9.09938 2.5 8.33333C2.5 6.78624 3.11458 5.30251 4.20854 4.20854C5.30251 3.11458 6.78624 2.5 8.33333 2.5C9.88043 2.5 11.3642 3.11458 12.4581 4.20854C13.5521 5.30251 14.1667 6.78624 14.1667 8.33333Z"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
></path>
</svg>
</button>
<button class="gh-burger"></button>
</div>
<nav class="gh-head-menu">
<ul class="nav">
<li class="nav-alreysyh">
<a href="https://nostrarabia.npub.pro/"
>الرئيسية</a
>
</li>
<li class="nav-nwstr-arby">
<a
href="https://nostrarabia.npub.pro/tag/nostrarabia/"
>نوستر عربي</a
>
</li>
<li class="nav-talm-albytkwyn">
<a
href="https://nostrarabia.npub.pro/tag/learnbitcoin/"
>تعلّم البيتكوين</a
>
</li>
<li class="nav-talm-alaqtsad-alnmsawy">
<a
href="https://nostrarabia.npub.pro/tag/Learnaustrianeconomics/"
>تعلّم الاقتصاد النمساوي</a
>
</li>
<li class="nav-alaadhaa">
<a
href="https://nostrarabia.npub.pro/tag/community/"
>الأعضاء</a
>
</li>
<li class="nav-akhbar">
<a href="https://nostrarabia.npub.pro/tag/news/"
>أخبار</a
>
</li>
<button
class="nav-more-toggle gh-icon-btn"
aria-label="More"
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 32 32"
fill="currentColor"
>
<path
d="M21.333 16c0-1.473 1.194-2.667 2.667-2.667v0c1.473 0 2.667 1.194 2.667 2.667v0c0 1.473-1.194 2.667-2.667 2.667v0c-1.473 0-2.667-1.194-2.667-2.667v0zM13.333 16c0-1.473 1.194-2.667 2.667-2.667v0c1.473 0 2.667 1.194 2.667 2.667v0c0 1.473-1.194 2.667-2.667 2.667v0c-1.473 0-2.667-1.194-2.667-2.667v0zM5.333 16c0-1.473 1.194-2.667 2.667-2.667v0c1.473 0 2.667 1.194 2.667 2.667v0c0 1.473-1.194 2.667-2.667 2.667v0c-1.473 0-2.667-1.194-2.667-2.667v0z"
></path>
</svg>
<div class="gh-dropdown">
<li class="nav-mymz">
<a
href="https://nostrarabia.npub.pro/tag/memes/"
>ميمز</a
>
</li>
<li class="nav-adwat">
<a
href="https://nostrarabia.npub.pro/tag/tools/"
>أدوات</a
>
</li>
<li class="nav-idamna">
<a
href="https://nostrarabia.npub.pro/tag/supportus/"
>إدعمنا</a
>
</li>
</div>
</button>
</ul>
</nav>
<div class="gh-head-actions">
<button
class="gh-search gh-icon-btn"
aria-label="Search this site"
data-ghost-search=""
>
<svg
width="20"
height="20"
viewBox="0 0 20 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M17.5 17.5L12.5 12.5L17.5 17.5ZM14.1667 8.33333C14.1667 9.09938 14.0158 9.85792 13.7226 10.5657C13.4295 11.2734 12.9998 11.9164 12.4581 12.4581C11.9164 12.9998 11.2734 13.4295 10.5657 13.7226C9.85792 14.0158 9.09938 14.1667 8.33333 14.1667C7.56729 14.1667 6.80875 14.0158 6.10101 13.7226C5.39328 13.4295 4.75022 12.9998 4.20854 12.4581C3.66687 11.9164 3.23719 11.2734 2.94404 10.5657C2.65088 9.85792 2.5 9.09938 2.5 8.33333C2.5 6.78624 3.11458 5.30251 4.20854 4.20854C5.30251 3.11458 6.78624 2.5 8.33333 2.5C9.88043 2.5 11.3642 3.11458 12.4581 4.20854C13.5521 5.30251 14.1667 6.78624 14.1667 8.33333Z"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
></path>
</svg>
</button>
</div>
</div>
</header>
<main class="gh-main gh-outer">
<div class="gh-inner">
<article class="gh-article post tag-nostrarabia no-image">
<section class="gh-content gh-canvas">
<np-content
event="note1dplcmt2885l9tsy6c4xenm3w7hrg92af42sz76p0ypd7ft0muvpqg80sqn"
><p>
من الجميل في نوستر هي قدرة التطبيقات على
التخاطر.<br /><br />إذا بتفضلوا استخدام
مساحة مثل الDiscord، ضيفوا صفحتنا على
flotilla.social للتواصل بصيغة التشات.
<br /><br />اضغط إضافة مساحة Add Space،<br />ثم
الدخول على مساحة Join a space،<br />وضيفوا
ريلاي relay.nostrarabia.com<br /><br />نراكم.
🫡<br /><br /><a href="/tag/nostrarabia/"
>#nostrarabia</a
>
</p>
</np-content>
<style>
np-map {
margin: 0.5rem 0;
}
np-content p {
margin-bottom: 2rem;
}
np-content h1 {
margin-top: 4rem;
margin-bottom: 2rem;
}
np-content h2 {
margin-top: 4rem;
margin-bottom: 2rem;
}
np-content h3 {
margin-top: 4rem;
margin-bottom: 2rem;
}
np-content h4 {
margin-top: 4rem;
margin-bottom: 2rem;
}
np-content h5 {
margin-top: 4rem;
margin-bottom: 2rem;
}
np-content h6 {
margin-top: 4rem;
margin-bottom: 2rem;
}
</style>
<np-content-cta
data-cta-list="zap,like,share"
data-cta-main="zap"
data-button-color="#139ad4"
data-text-button-color="#fff"
data-event-addr="note1dplcmt2885l9tsy6c4xenm3w7hrg92af42sz76p0ypd7ft0muvpqg80sqn"
data-event-id="note1dplcmt2885l9tsy6c4xenm3w7hrg92af42sz76p0ypd7ft0muvpqg80sqn"
data-author-npub="npub1syk07kh6tkwrksyzhqk8qdjul5kj08p842gjxyacljlavhzq4m4slmdu3p"
id="np-content-cta"
></np-content-cta>
<div
style="display: none"
id="zap-button"
data-anon=""
data-npub="npub1syk07kh6tkwrksyzhqk8qdjul5kj08p842gjxyacljlavhzq4m4slmdu3p"
data-note-id="note1dplcmt2885l9tsy6c4xenm3w7hrg92af42sz76p0ypd7ft0muvpqg80sqn"
data-relays="wss://purplepag.es/,wss://user.kindpag.es/,wss://relay.nos.social/,wss://relay.primal.net/,wss://relay.damus.io/,wss://relay.nostrarabia.com/"
data-button-color="#139ad4"
data-amount=""
></div>
<np-content-comments
data-id="note1dplcmt2885l9tsy6c4xenm3w7hrg92af42sz76p0ypd7ft0muvpqg80sqn"
data-relays="wss://purplepag.es/,wss://user.kindpag.es/,wss://relay.nos.social/,wss://relay.primal.net/,wss://relay.damus.io/,wss://relay.nostrarabia.com/"
data-client="nostrarabia.npub.pro"
></np-content-comments
><np-content-dm
data-peer-npub="npub1syk07kh6tkwrksyzhqk8qdjul5kj08p842gjxyacljlavhzq4m4slmdu3p"
data-relays="wss://purplepag.es/,wss://user.kindpag.es/,wss://relay.nos.social/,wss://relay.primal.net/,wss://relay.damus.io/,wss://relay.nostrarabia.com/"
></np-content-dm>
<header class="gh-article-header">
<h1 class="gh-article-title">
من الجميل في نوستر هي قدرة…
</h1>
</header>
<aside class="gh-article-meta">
<div class="gh-article-meta-inner">
<figure class="gh-author-image">
<img
src="https://mir-s3-cdn-cf.behance.net/projects/404/01ffa529646977.55fca90cee60d.jpg"
alt="نوستر بالعربي"
/>
</figure>
<div class="gh-article-meta-wrapper">
<h4 class="gh-author-name">
<a
href="/author/npub1syk07kh6tkwrksyzhqk8qdjul5kj08p842gjxyacljlavhzq4m4slmdu3p/"
>نوستر بالعربي</a
>
</h4>
<time
class="gh-article-date"
datetime="2024-12-01"
>Dec 01, 2024</time
>
</div>
<!-- <a
class="gh-article-tag"
href="/tag/nostrarabia/"
style="--tag-color:"
>nostrarabia</a
> -->
</div>
</aside>
</section>
<footer class="gh-article-footer gh-canvas">
<nav class="gh-navigation">
<div class="gh-navigation-previous">
<a
class="gh-navigation-link"
href="/post/1733010811046/"
>← Previous</a
>
</div>
<div class="gh-navigation-middle"></div>
<div class="gh-navigation-next">
<a
class="gh-navigation-link"
href="/post/1733278250434/"
>Next →</a
>
</div>
</nav>
</footer>
</article>
</div>
</main>
<footer class="gh-foot gh-outer">
<div class="gh-foot-inner gh-inner">
<nav class="gh-foot-menu"></nav>
<div class="gh-copyright">
نوستر © 2024. Powered by
<a
href="https://npub.pro/"
target="_blank"
rel="noopener"
>Npub.pro</a
>. Theme by
<a
href="https://ghost.org/"
target="_blank"
rel="noopener"
>Ghost</a
>
</div>
</div>
</footer>
</div>
<div class="pswp" tabindex="-1" role="dialog" aria-hidden="true">
<div class="pswp__bg"></div>
<div class="pswp__scroll-wrap">
<div class="pswp__container">
<div class="pswp__item"></div>
<div class="pswp__item"></div>
<div class="pswp__item"></div>
</div>
<div class="pswp__ui pswp__ui--hidden">
<div class="pswp__top-bar">
<div class="pswp__counter"></div>
<button
class="pswp__button pswp__button--close"
title="Close (Esc)"
></button>
<button
class="pswp__button pswp__button--share"
title="Share"
></button>
<button
class="pswp__button pswp__button--fs"
title="Toggle fullscreen"
></button>
<button
class="pswp__button pswp__button--zoom"
title="Zoom in/out"
></button>
<div class="pswp__preloader">
<div class="pswp__preloader__icn">
<div class="pswp__preloader__cut">
<div class="pswp__preloader__donut"></div>
</div>
</div>
</div>
</div>
<div
class="pswp__share-modal pswp__share-modal--hidden pswp__single-tap"
>
<div class="pswp__share-tooltip"></div>
</div>
<button
class="pswp__button pswp__button--arrow--left"
title="Previous (arrow left)"
></button>
<button
class="pswp__button pswp__button--arrow--right"
title="Next (arrow right)"
></button>
<div class="pswp__caption">
<div class="pswp__caption__center"></div>
</div>
</div>
</div>
</div>
<script src="https://blossom.npubpro.com/64f7c3c5de348a7d1a5c7d1519abfa33fec8c5442c583fda441d25cd7b5990cf.js"></script>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/venobox@2.1.8/dist/venobox.min.css"
type="text/css"
media="screen"
/>
<script>
;(() => {
const script = document.createElement('script')
script.async = true
script.type = 'text/javascript'
script.src =
'https://cdn.jsdelivr.net/npm/venobox@2.1.8/dist/venobox.min.js'
script.onload = () => {
new VenoBox({
selector: '.vbx-media',
spinColor: '#139ad4',
overlayColor: '#139ad4',
})
}
document.body.appendChild(script)
})()
</script>
<script
async=""
type="text/javascript"
src="https://cdn.jsdelivr.net/npm/venobox@2.1.8/dist/venobox.min.js"
></script>
<script
type="text/javascript"
async=""
src="https://cdn.npubpro.com/zapthreads.iife.0.6.0.js"
></script>
<script
type="text/javascript"
async=""
src="https://cdn.npubpro.com/nostr-site-zapthreads.1.0.2.js"
></script>
<script
type="text/javascript"
async=""
src="https://cdn.npubpro.com/content-cta.iife.1.0.22.js"
></script>
<script
async=""
src="../packages/auth/dist/unpkg.js"
data-perms="sign_event:1,sign_event:7,sign_event:3,sign_event:9734,sign_event:10003,sign_event:9802,nip04_encrypt,nip04_decrypt"
data-start-screen="local-signup"
data-signup-relays="wss://relay.primal.net/,wss://relay.damus.io/,wss://relay.nostrarabia.com/"
></script>
<script>
;(async () => {
if (!window.nostrSite)
await new Promise((ok) =>
document.addEventListener('npLoad', ok),
)
const ep = window.nostrSite.plugins.register('nostr-login')
document.addEventListener('nlAuth', async (e) => {
console.log('nlAuth', e)
ep.dispatch('auth', {
type: e.detail.type,
pubkey: e.detail.pubkey,
})
if (
e.detail.type === 'login' ||
e.detail.type === 'signup'
) {
window.__nlAuthed = true
} else {
window.__nlAuthed = false
}
const npub = window.nostrSite.nostrTools.nip19.npubEncode(
await window.nostr.getPublicKey(),
)
const zapButton = document.querySelector('#zap-button')
if (zapButton) {
if (window.__nlAuthed)
zapButton.setAttribute('data-anon', '')
else zapButton.setAttribute('data-anon', 'true')
}
})
})()
</script>
<script src="https://cdn.npubpro.com/nostr-zap.0.22.2.js"></script>
<div></div>
<np-content-cta-selection></np-content-cta-selection>
<script>
;(async () => {
if (!window.nostrSite)
await new Promise((ok) =>
document.addEventListener('npLoad', ok),
)
const ep = window.nostrSite.plugins.register('nostr-zap')
console.log('nostr-zap ep', ep)
ep.subscribe('action-zap', (amount) => {
const button = document.querySelector('#zap-button')
button.setAttribute('data-amount', amount || '')
button.dispatchEvent(new Event('click'))
})
document.addEventListener('nostr-zap-published', (e) => {
console.log('nostr-zap on zap published', e)
ep.dispatch('event-published', e.detail)
})
})()
</script>
<script
async=""
src="https://unpkg.com/nostr-site-search@1.0.12/dist/index.js"
></script>
<script>
document.addEventListener('np-search-goto', (e) => {
console.log('np-search-goto', e)
window.location.href = e.detail
})
</script>
<script
async=""
src="https://cdn.npubpro.com/embeds.iife.1.0.4.js"
></script>
<script
type="text/javascript"
src="https://cdn.npubpro.com/maptalks.min.js"
></script>
<script>
const container = document.querySelector('np-map')
console.log('map', container)
if (container) {
const coords = container
.getAttribute('coords')
.split(',')
.map((c) => Number(c))
console.log('coords', coords)
const div = document.createElement('div')
div.style.width = '100%'
div.style.height = '300px'
container.append(div)
const map = new maptalks.Map(div, {
center: coords,
zoom: 15,
zoomControl: true, // add zoom control
scaleControl: true, // add scale control
baseLayer: new maptalks.TileLayer('base', {
// urlTemplate: 'https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png',
urlTemplate:
'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
subdomains: ['a', 'b', 'c'], // "d"
attribution:
'&copy; <a href="http://osm.org">OpenStreetMap</a>',
}),
})
const point = new maptalks.Marker(coords, {
visible: true,
editable: false,
cursor: 'pointer',
draggable: false,
// symbol : {
// 'textFaceName' : 'sans-serif',
// 'textName' : 'MapTalks',
// 'textFill' : '#34495e',
// 'textHorizontalAlignment' : 'right',
// 'textSize' : 40
// }
})
point.on('click touchend', (e) => console.log(e))
new maptalks.VectorLayer('vector', point).addTo(map)
}
</script>
<script
type="text/javascript"
src="https://cdn.npubpro.com/index.js"
onload="window.nostrSite.startTab();"
></script>
<style>
#pwa-toast {
visibility: hidden;
position: fixed;
right: 0;
bottom: 0;
margin: 16px;
padding: 12px;
border: 1px solid #8885;
border-radius: 4px;
z-index: 1;
text-align: left;
box-shadow: 3px 4px 5px 0 #8885;
display: grid;
background-color: #fff;
}
#pwa-toast .message {
margin-bottom: 8px;
}
#pwa-toast .buttons {
display: flex;
}
#pwa-toast button {
border: 1px solid #8885;
outline: none;
margin-right: 5px;
border-radius: 2px;
padding: 3px 10px;
}
#pwa-toast.show {
visibility: visible;
}
button#pwa-refresh {
display: none;
}
#pwa-toast.show.refresh button#pwa-refresh {
display: block;
}
</style>
<iframe
width="0"
height="0"
border="0"
id="__nostr-login-worker-iframe-3-use-nsec-app"
style="display: none"
src="https://3.use.nsec.app/iframe"
></iframe
><deepl-input-controller></deepl-input-controller>
</body>
</html>

View File

@@ -1,343 +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 Interactive Test</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
.container { max-width: 800px; margin: 0 auto; }
.controls { background: #f5f5f5; padding: 20px; border-radius: 8px; margin-bottom: 20px; }
.control-group { margin-bottom: 15px; }
.control-group label { display: inline-block; width: 150px; font-weight: bold; }
.control-group input, .control-group select { margin-left: 10px; padding: 5px; }
.checkbox-group { display: flex; gap: 10px; flex-wrap: wrap; }
.checkbox-group label { width: auto; margin-right: 15px; }
.current-config { background: #e8f4fd; padding: 15px; border-radius: 5px; margin-bottom: 20px; }
.login-area { background: #fff; border: 2px solid #ddd; padding: 20px; border-radius: 8px; }
button { padding: 10px 15px; margin: 5px; cursor: pointer; }
.apply-btn { background: #007cba; color: white; border: none; border-radius: 5px; }
.reset-btn { background: #999; color: white; border: none; border-radius: 5px; }
.note { background: #fff3cd; padding: 10px; border-radius: 5px; margin-top: 10px; font-size: 14px; }
</style>
</head>
<body>
<div class="container">
<h1>Nostr Login Interactive Test</h1>
<!-- Controls Section -->
<div class="controls">
<h3>Configuration Options</h3>
<div class="control-group">
<label>Theme:</label>
<select id="themeSelect">
<option value="default">Default</option>
<option value="ocean">Ocean</option>
<option value="lemonade">Lemonade</option>
<option value="purple">Purple</option>
<option value="laan">Laan</option>
</select>
</div>
<div class="control-group">
<label>Dark Mode:</label>
<input type="checkbox" id="darkModeCheck">
</div>
<div class="control-group">
<label>Start Screen:</label>
<select id="startScreenSelect">
<option value="">Default</option>
<option value="welcome">Welcome</option>
<option value="welcome-login">Welcome Login</option>
<option value="welcome-signup">Welcome Signup</option>
<option value="signup">Signup</option>
<option value="local-signup">Local Signup</option>
<option value="login">Login</option>
<option value="connect">Connect</option>
<option value="login-bunker-url">Login Bunker URL</option>
<option value="login-read-only">Login Read Only</option>
<option value="switch-account">Switch Account</option>
</select>
</div>
<div class="control-group">
<label>Auth Methods:</label>
<div class="checkbox-group">
<label><input type="checkbox" value="connect" checked> Connect (NIP-46)</label>
<label><input type="checkbox" value="extension" checked> Extension</label>
<label><input type="checkbox" value="readOnly" checked> Read Only</label>
<label><input type="checkbox" value="local" checked> Local</label>
</div>
</div>
<div class="control-group">
<label>No Banner:</label>
<input type="checkbox" id="noBannerCheck">
</div>
<div class="control-group">
<label>Bunkers:</label>
<input type="text" id="bunkersInput" placeholder="e.g., nsec.app,highlighter.com" style="width: 300px;">
</div>
<div class="control-group">
<label>Permissions:</label>
<input type="text" id="permsInput" placeholder="e.g., sign_event:1,nip04_encrypt" style="width: 300px;">
</div>
<div class="control-group">
<label>Title:</label>
<input type="text" id="titleInput" placeholder="Custom welcome title" style="width: 300px;">
</div>
<div class="control-group">
<label>Description:</label>
<input type="text" id="descriptionInput" placeholder="Custom welcome description" style="width: 300px;">
</div>
<button class="apply-btn" onclick="applyConfig()">Apply Configuration</button>
<button class="reset-btn" onclick="resetConfig()">Reset to Defaults</button>
<div class="note">
<strong>Note:</strong> Configuration changes will be applied by reloading the page with new settings.
</div>
</div>
<!-- Current Configuration Display -->
<div class="current-config">
<h4>Current Configuration:</h4>
<pre id="configDisplay"></pre>
</div>
<!-- Login/User Section -->
<div class="login-area">
<!-- Login Section -->
<div id="loginSection">
<h3>Test Login</h3>
<button id="loginBtn">Login with Nostr</button>
<button onclick="launchSpecificScreen()">Launch with Start Screen</button>
</div>
<!-- User Info Section (hidden initially) -->
<div id="userSection" style="display: none;">
<h3>Welcome!</h3>
<p><strong>Your Public Key (hex):</strong></p>
<div id="pubkeyHex" style="word-break: break-all; background: #f0f0f0; padding: 10px; border-radius: 4px;"></div>
<br>
<button id="logoutBtn">Logout</button>
</div>
</div>
</div>
<!-- Dynamic script loading with configuration -->
<script>
let currentConfig = {};
let scriptLoaded = false;
// Load nostr-login with current configuration
function loadNostrLogin() {
if (scriptLoaded) return;
const script = document.createElement('script');
script.src = '../packages/auth/dist/unpkg.js';
// Apply configuration as data attributes
const config = getStoredConfig();
if (config.theme) script.setAttribute('data-theme', config.theme);
if (config.darkMode !== undefined) script.setAttribute('data-dark-mode', config.darkMode);
if (config.startScreen) script.setAttribute('data-start-screen', config.startScreen);
if (config.noBanner !== undefined) script.setAttribute('data-no-banner', config.noBanner);
if (config.bunkers) script.setAttribute('data-bunkers', config.bunkers);
if (config.perms) script.setAttribute('data-perms', config.perms);
if (config.title) script.setAttribute('data-title', config.title);
if (config.description) script.setAttribute('data-description', config.description);
if (config.methods && config.methods.length > 0) {
script.setAttribute('data-methods', config.methods.join(','));
}
document.head.appendChild(script);
scriptLoaded = true;
// Wait for script to load, then set up event listeners
script.onload = () => {
setupEventListeners();
};
}
// Get stored configuration from localStorage
function getStoredConfig() {
try {
return JSON.parse(localStorage.getItem('nostrLoginConfig') || '{}');
} catch {
return {};
}
}
// Store configuration in localStorage
function storeConfig(config) {
localStorage.setItem('nostrLoginConfig', JSON.stringify(config));
}
// Listen for nostr-login auth events
function setupEventListeners() {
document.addEventListener('nlAuth', async (e) => {
console.log('nlAuth event:', e.detail);
if (e.detail.type === 'login' || e.detail.type === 'signup') {
await showUserInfo();
} else if (e.detail.type === 'logout') {
showLoginSection();
}
});
}
// Get and display user info
async function showUserInfo() {
try {
const pubkey = await window.nostr.getPublicKey();
console.log('Got pubkey:', pubkey);
// Display pubkey
document.getElementById('pubkeyHex').textContent = pubkey;
// Hide login section, show user section
document.getElementById('loginSection').style.display = 'none';
document.getElementById('userSection').style.display = 'block';
} catch (error) {
console.error('Failed to get pubkey:', error);
}
}
// Show login section
function showLoginSection() {
document.getElementById('loginSection').style.display = 'block';
document.getElementById('userSection').style.display = 'none';
document.getElementById('pubkeyHex').textContent = '';
}
// Apply configuration
function applyConfig() {
// Collect all settings
const config = {
theme: document.getElementById('themeSelect').value,
darkMode: document.getElementById('darkModeCheck').checked,
startScreen: document.getElementById('startScreenSelect').value,
noBanner: document.getElementById('noBannerCheck').checked,
bunkers: document.getElementById('bunkersInput').value,
perms: document.getElementById('permsInput').value,
title: document.getElementById('titleInput').value,
description: document.getElementById('descriptionInput').value,
methods: Array.from(document.querySelectorAll('input[type="checkbox"][value]'))
.filter(cb => cb.checked)
.map(cb => cb.value)
};
// Remove empty values
Object.keys(config).forEach(key => {
if (config[key] === '' || (Array.isArray(config[key]) && config[key].length === 0)) {
delete config[key];
}
});
currentConfig = config;
storeConfig(config);
updateConfigDisplay();
console.log('Applied config:', config);
// Reload page to apply new configuration
window.location.reload();
}
// Reset configuration
function resetConfig() {
document.getElementById('themeSelect').value = 'default';
document.getElementById('darkModeCheck').checked = false;
document.getElementById('startScreenSelect').value = '';
document.getElementById('noBannerCheck').checked = false;
document.getElementById('bunkersInput').value = '';
document.getElementById('permsInput').value = '';
document.getElementById('titleInput').value = '';
document.getElementById('descriptionInput').value = '';
// Reset auth methods to all checked
document.querySelectorAll('input[type="checkbox"][value]').forEach(cb => cb.checked = true);
currentConfig = {};
localStorage.removeItem('nostrLoginConfig');
updateConfigDisplay();
}
// Update config display
function updateConfigDisplay() {
document.getElementById('configDisplay').textContent = JSON.stringify(currentConfig, null, 2);
}
// Launch with specific screen
function launchSpecificScreen() {
const startScreen = document.getElementById('startScreenSelect').value || 'welcome';
document.dispatchEvent(new CustomEvent('nlLaunch', { detail: startScreen }));
}
// Load configuration from storage and update UI
function loadConfigIntoUI() {
const config = getStoredConfig();
currentConfig = config;
// Set default to laan theme for testing
if (config.theme) document.getElementById('themeSelect').value = config.theme;
else document.getElementById('themeSelect').value = 'laan';
if (config.darkMode !== undefined) document.getElementById('darkModeCheck').checked = config.darkMode;
if (config.startScreen) document.getElementById('startScreenSelect').value = config.startScreen;
if (config.noBanner !== undefined) document.getElementById('noBannerCheck').checked = config.noBanner;
if (config.bunkers) document.getElementById('bunkersInput').value = config.bunkers;
if (config.perms) document.getElementById('permsInput').value = config.perms;
if (config.title) document.getElementById('titleInput').value = config.title;
if (config.description) document.getElementById('descriptionInput').value = config.description;
// Load auth methods
document.querySelectorAll('input[type="checkbox"][value]').forEach(cb => {
cb.checked = !config.methods || config.methods.includes(cb.value);
});
updateConfigDisplay();
}
// Handle login button click
document.addEventListener('DOMContentLoaded', () => {
// Load configuration into UI
loadConfigIntoUI();
// Load nostr-login with current config
loadNostrLogin();
document.getElementById('loginBtn').addEventListener('click', () => {
// Trigger nostr-login modal
document.dispatchEvent(new CustomEvent('nlLaunch', { detail: 'welcome' }));
});
// Handle logout button click
document.getElementById('logoutBtn').addEventListener('click', () => {
// Trigger logout
document.dispatchEvent(new Event('nlLogout'));
});
// Update config display when inputs change
document.querySelectorAll('input, select').forEach(input => {
input.addEventListener('change', () => {
// Auto-apply dark mode for immediate feedback
if (input.id === 'darkModeCheck') {
document.dispatchEvent(new CustomEvent('nlDarkMode', { detail: input.checked }));
}
});
});
});
</script>
</body>
</html>

View File

@@ -1,135 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Input and Output Fields</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 20px;
}
.container {
max-width: 400px;
margin: auto;
}
input, button, textarea {
width: 100%;
padding: 10px;
margin: 10px 0;
box-sizing: border-box;
}
.button-container {
display: flex;
flex-wrap: wrap;
gap: 10px; /* Adds space between the buttons */
}
.button-container button {
flex: 1 1 calc(50% - 10px); /* Makes each button take up half the width minus the gap */
box-sizing: border-box;
}
</style>
<script src='https://www.unpkg.com/nostr-login@latest/dist/unpkg.js'></script>
</head>
<body>
<div class="container">
<h1>Try Nostr-Login</h1>
<textarea id="inputField" rows="4" placeholder="Paste your json formatted event here"></textarea>
<label>Output:</label>
<textarea id="outputField" rows="4" placeholder="Output will be shown here" readonly></textarea>
<button onclick="fillExampleData()">Fill with example event</button>
<button onclick="SignEventFn()">Sign Event</button>
<div class="button-container">
<button onclick="cryptWithNIP04('encrypt')">Encrypt with NIP-04</button>
<button onclick="cryptWithNIP04('decrypt')">Decrypt with NIP-04</button>
<button onclick="cryptWithNIP44('encrypt')">Encrypt with NIP-44</button>
<button onclick="cryptWithNIP44('decrypt')">Decrypt with NIP-44</button>
</div>
<button onclick="switchOutputInput()">Switch Output Input</button>
</div>
<form id="testpw" action="/usage.html" method="POST">
<input type="text" name="name" value="npub">
<input type="password" name="password" value="a;h123UIFBEKSDBF,SA">
<button type="submit">Submit</button>
</form>
<script>
const form = document.querySelector("#testpw");
form.onsubmit = (e) => {
e.preventDefault();
}
</script>
<script>
// Example Nostr event as json string
var ExampleData = `{ "content": "hello world", "created_at": 1731313613, "id": "", "kind": 1, "pubkey": "568ad8bf00ed530eb44614e4b363271f36f6b645700630470c51f98e7e58fbf0", "tags": [] }`
// Function to get public key
async function getPublicKey() {
return await window.nostr.getPublicKey();
}
// Function to encrypt with NIP-04
async function cryptWithNIP04(encryptType) {
try {
var publicKey = await getPublicKey();
var data = document.getElementById('inputField').value;
var result;
if (encryptType === "encrypt") {
result = await window.nostr.nip04.encrypt(publicKey, data);
} else if (encryptType === "decrypt") {
result = await window.nostr.nip04.decrypt(publicKey, data);
}
document.getElementById('outputField').value = result;
} catch (error) {
console.error("Error in cryptWithNIP04:", error);
}
}
// Function to encrypt with NIP-44
async function cryptWithNIP44(encryptType) {
try {
var publicKey = await getPublicKey();
var data = document.getElementById('inputField').value;
var result;
if (encryptType === "encrypt") {
result = await window.nostr.nip44.encrypt(publicKey, data);
} else if (encryptType === "decrypt") {
result = await window.nostr.nip44.decrypt(publicKey, data);
}
document.getElementById('outputField').value = result;
} catch (error) {
console.error("Error in cryptWithNIP44:", error);
}
}
// Sign nostr event
async function SignEventFn() {
try {
var input = document.getElementById('inputField').value;
// Convert the text from input to json object
var json_data = JSON.parse(input);
// Sign the event object using the plugin signEvent method
var signedEvent = await window.nostr.signEvent(json_data);
document.getElementById('outputField').value = JSON.stringify(signedEvent, null, 2);
} catch (error) {
console.error("Error in SignEventFn:", error);
}
}
// Fill example data for easy testing
function fillExampleData() {
document.getElementById('outputField').value = '';
document.getElementById('inputField').value = ExampleData;
}
// Switch input and output fields
function switchOutputInput() {
var tempOutput = document.getElementById('outputField').value;
document.getElementById('inputField').value = tempOutput;
document.getElementById('outputField').value = '';
}
</script>
</body>
</html>