Compare commits
14 Commits
2c81078e77
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 3576418ba9 | |||
|
|
2dd172a0c1 | ||
| ae81a6bd7f | |||
| 8414e1013d | |||
| c4914a5ae9 | |||
| 70c85bad7e | |||
| 6176fe8eac | |||
| 6ffd877f9b | |||
| 87ecb447db | |||
| 0969fef3a9 | |||
| 7a32ae3900 | |||
| 200d6a6d7f | |||
| ce4f00dc58 | |||
| 5a56ab5aa0 |
@@ -11,29 +11,32 @@ const links = [
|
||||
"https://mempool.btcforplebs.com",
|
||||
"https://lightning.btcforplebs.com",
|
||||
"https://nostrudel.btcforplebs.com",
|
||||
"https://nosotros.btcforplebs.com",
|
||||
"https://mint.btcforplebs.com",
|
||||
"https://cashu.btcforplebs.com",
|
||||
"https://fevela.btcforplebs.com",
|
||||
"https://nosotros.btcforplebs.com",
|
||||
"https://coracle.btcforplebs.com",
|
||||
"https://shipyard.btcforplebs.com",
|
||||
"https://live.btcforplebs.com",
|
||||
"https://nsec.btcforplebs.com",
|
||||
"https://flotilla.btcforplebs.com",
|
||||
"https://ncryptdesec.btcforplebs.com",
|
||||
"https://community.btcforplebs.com",
|
||||
"https://nutstash.btcforplebs.com",
|
||||
"https://jumble.btcforplebs.com"
|
||||
];
|
||||
|
||||
// Optional: Only allow internal requests if you're not exposing this publicly
|
||||
app.use((req, res, next) => {
|
||||
const origin = req.headers.origin || 'unknown';
|
||||
console.log(`Request: ${req.method} ${req.url} | Origin: ${origin}`);
|
||||
res.header("Access-Control-Allow-Headers", "Content-Type");
|
||||
res.header("Access-Control-Allow-Origin", "*");
|
||||
res.header("Access-Control-Allow-Methods", "GET, OPTIONS");
|
||||
|
||||
if (req.method === 'OPTIONS') {
|
||||
return res.sendStatus(200);
|
||||
}
|
||||
next();
|
||||
app.use((req, res, next) => {
|
||||
const origin = req.headers.origin || 'unknown';
|
||||
console.log(`Request: ${req.method} ${req.url} | Origin: ${origin}`);
|
||||
res.header("Access-Control-Allow-Headers", "Content-Type");
|
||||
res.header("Access-Control-Allow-Origin", "*");
|
||||
res.header("Access-Control-Allow-Methods", "GET, OPTIONS");
|
||||
|
||||
if (req.method === 'OPTIONS') {
|
||||
return res.sendStatus(200);
|
||||
}
|
||||
next();
|
||||
});
|
||||
|
||||
app.get("/", (req, res) => {
|
||||
|
||||
@@ -611,3 +611,68 @@ footer {
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
/* Navbar Styles */
|
||||
.navbar {
|
||||
background: linear-gradient(to bottom, var(--button-bg-top), var(--button-bg-bottom));
|
||||
border-bottom: 1px solid var(--button-border);
|
||||
padding: 10px 20px;
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.navbar-container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.navbar-logo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
text-decoration: none;
|
||||
color: var(--text-color);
|
||||
font-weight: bold;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.navbar-logo img {
|
||||
height: 40px;
|
||||
margin-right: 10px;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.navbar-menu {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.navbar-item {
|
||||
text-decoration: none;
|
||||
color: var(--text-color);
|
||||
font-weight: 500;
|
||||
padding: 8px 12px;
|
||||
border-radius: 6px;
|
||||
transition: background-color 0.2s, color 0.2s;
|
||||
}
|
||||
|
||||
.navbar-item:hover {
|
||||
background-color: rgba(0, 0, 0, 0.05);
|
||||
color: #F7931A; /* Bitcoin Orange */
|
||||
}
|
||||
|
||||
/* Mobile Navbar */
|
||||
@media (max-width: 768px) {
|
||||
.navbar-container {
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.navbar-menu {
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
gap: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,11 +24,11 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
folder.style.display = 'block';
|
||||
button.classList.add('active');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Footer Loader and Event Listeners
|
||||
// Footer Loader and Event Listeners
|
||||
fetch('/parts/footer.html')
|
||||
.then(response => response.text())
|
||||
.then(data => {
|
||||
@@ -62,25 +62,4 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
}
|
||||
})
|
||||
.catch(error => console.error('Error loading footer:', error));
|
||||
|
||||
// --- 5. Fetch and Display Last Update from GitHub -----------------------
|
||||
fetch('https://api.github.com/repos/btcforplebs/BTCforPlebs.com/commits/main')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
const lastUpdate = new Date(data.commit.author.date);
|
||||
const formattedDate = lastUpdate.toLocaleDateString();
|
||||
const formattedTime = lastUpdate.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
|
||||
|
||||
const updateText = document.getElementById('last-updated-text');
|
||||
if (updateText) {
|
||||
updateText.textContent = `Website last updated: ${formattedDate} ${formattedTime}`;
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error fetching last update:', error);
|
||||
const updateText = document.getElementById('last-updated-text');
|
||||
if (updateText) {
|
||||
updateText.textContent = 'Last update: Error fetching data.';
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,151 +1,178 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>BTCforPlebs</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>BTCforPlebs</title>
|
||||
|
||||
<!-- Meta Description -->
|
||||
<meta name="description" content="BTCforPlebs is a resource to help ordinary people learn about Bitcoin. Explore guides, tools, and resources to start your Bitcoin journey.">
|
||||
<!-- Meta Description -->
|
||||
<meta name="description"
|
||||
content="BTCforPlebs is a resource to help ordinary people learn about Bitcoin. Explore guides, tools, and resources to start your Bitcoin journey.">
|
||||
|
||||
<!-- Open Graph Meta Tags -->
|
||||
<meta property="og:title" content="BTCforPlebs">
|
||||
<meta property="og:description" content="A place to help ordinary people learn about Bitcoin">
|
||||
<meta property="og:image" content="images/thumb.jpeg">
|
||||
<meta property="og:url" content="https://btcforplebs.com">
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<!-- Open Graph Meta Tags -->
|
||||
<meta property="og:title" content="BTCforPlebs">
|
||||
<meta property="og:description" content="A place to help ordinary people learn about Bitcoin">
|
||||
<meta property="og:image" content="images/thumb.jpeg">
|
||||
<meta property="og:url" content="https://btcforplebs.com">
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
|
||||
<!-- favicon -->
|
||||
<link rel="icon" href="images/favicon.png" type="image/png">
|
||||
<!-- favicon -->
|
||||
<link rel="icon" href="images/favicon.png" type="image/png">
|
||||
|
||||
<!-- scripts -->
|
||||
<script src="assets/js/scripts.js"></script>
|
||||
<link rel="stylesheet" href="assets/css/main.css">
|
||||
<link rel="apple-touch-icon" href="images/apple-touch-icon.png">
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.6.0/css/all.min.css" rel="stylesheet">
|
||||
<!-- scripts -->
|
||||
<script src="assets/js/scripts.js"></script>
|
||||
<link rel="stylesheet" href="assets/css/main.css">
|
||||
<link rel="apple-touch-icon" href="images/apple-touch-icon.png">
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.6.0/css/all.min.css" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<!-- Bitcoin Price Banner -->
|
||||
|
||||
<div class="container">
|
||||
<body>
|
||||
|
||||
<!-- Bitcoin Price Banner -->
|
||||
|
||||
<div class="container">
|
||||
<h1>Welcome to BTCforPlebs</h1>
|
||||
<h3>A place to help ordinary people learn about Bitcoin</h3>
|
||||
<img src="/images/btcforplebs_profile.gif" alt="Profile Picture" class="profile-picture">
|
||||
|
||||
<!-- Learn Links (unchanged) -->
|
||||
<a href="/learn-bitcoin" class="button prefetch">Learn About Bitcoin</a>
|
||||
<a href="/learn-nostr" class="button prefetch">Learn About Nostr</a>
|
||||
<a href="/learn-cashu" class="button prefetch">Learn About Cashu</a>
|
||||
<a href="/bitcoin-and-the-bible" class="button prefetch">Bitcoin & The Bible</a>
|
||||
<!-- Learn Links (unchanged) -->
|
||||
<a href="/learn-bitcoin" class="button prefetch">Learn About Bitcoin</a>
|
||||
<a href="/learn-nostr" class="button prefetch">Learn About Nostr</a>
|
||||
<a href="/learn-cashu" class="button prefetch">Learn About Cashu</a>
|
||||
<a href="/bitcoin-and-the-bible" class="button prefetch">Bitcoin & The Bible</a>
|
||||
|
||||
<!-- Bitcoin Links -->
|
||||
<button class="button" id="bitcoin-folder-btn" onclick="toggleFolder('folder2', 'bitcoin-folder-btn')">Use Bitcoin <span style="color: #F7931A;">←</span></button>
|
||||
<div class="links" id="folder2">
|
||||
<a href="https://lightning.btcforplebs.com" target="_blank" class="prefetch">Lightning <span class="status-emoji" data-url="https://lightning.btcforplebs.com">⏳</span></a>
|
||||
<a href="https://mempool.btcforplebs.com" target="_blank" class="prefetch">Mempool <span class="status-emoji" data-url="https://mempool.btcforplebs.com">⏳</span></a>
|
||||
<a href="https://bitfeed.live" class="prefetch">Bitfeed.Live ⚪️ </a>
|
||||
<a href="https://hodl.camp" class="prefetch">Hodl.Camp ⚪️ </a>
|
||||
<a href="https://bitview.space" class="prefetch">Bitview.Space ⚪️ </a>
|
||||
</div>
|
||||
<!-- Bitcoin Links -->
|
||||
<button class="button" id="bitcoin-folder-btn" onclick="toggleFolder('folder2', 'bitcoin-folder-btn')">Use Bitcoin
|
||||
<span style="color: #F7931A;">←</span></button>
|
||||
<div class="links" id="folder2">
|
||||
<a href="https://lightning.btcforplebs.com" target="_blank" class="prefetch">Lightning <span class="status-emoji"
|
||||
data-url="https://lightning.btcforplebs.com">⏳</span></a>
|
||||
<a href="https://mempool.btcforplebs.com" target="_blank" class="prefetch">Mempool <span class="status-emoji"
|
||||
data-url="https://mempool.btcforplebs.com">⏳</span></a>
|
||||
<a href="https://bitfeed.live" class="prefetch">Bitfeed.Live ⚪️ </a>
|
||||
<a href="https://hodl.camp" class="prefetch">Hodl.Camp ⚪️ </a>
|
||||
<a href="https://bitview.space" class="prefetch">Bitview.Space ⚪️ </a>
|
||||
</div>
|
||||
|
||||
<!-- Nostr Links -->
|
||||
<button class="button" id="nostr-folder-btn" onclick="toggleFolder('folder1', 'nostr-folder-btn')">Use Nostr <span style="color: #F7931A;">←</span></button>
|
||||
<div class="links" id="folder1">
|
||||
<a href="https://nsec.btcforplebs.com" target="_blank" class="prefetch">Nsec Bunker <span class="status-emoji" data-url="https://nsec.btcforplebs.com">⏳</span></a>
|
||||
<a href="https://bloom.btcforplebs.com" target="_blank" class="prefetch">Bloom <span class="status-emoji" data-url="https://bloom.btcforplebs.com">⏳</span></a>
|
||||
<a href="https://nostrudel.btcforplebs.com" target="_blank" class="prefetch">Nostrudel <span class="status-emoji" data-url="https://nostrudel.btcforplebs.com">⏳</span></a>
|
||||
<a href="https://nosotros.btcforplebs.com" target="_blank" class="prefetch">Nosotros <span class="status-emoji" data-url="https://nosotros.btcforplebs.com">⏳</span></a>
|
||||
<a href="https://flotilla.btcforplebs.com" target="_blank" class="prefetch">Flotilla <span class="status-emoji" data-url="https://flotilla.btcforplebs.com">⏳</span></a>
|
||||
<a href="https://Jumble.btcforplebs.com" target="_blank" class="prefetch">Jumble <span class="status-emoji" data-url="https://jumble.btcforplebs.com">⏳</span></a>
|
||||
<a href="https://shipyard.btcforplebs.com" target="_blank" class="prefetch">Shipyard <span class="status-emoji" data-url="https://shipyard.btcforplebs.com">⏳</span></a>
|
||||
<a href="/relay" target="_blank" class="prefetch">Relay <span class="status-emoji" data-url="https://relay.btcforplebs.com">⏳</span></a>
|
||||
<a href="https://nostrapps.com" target="_blank">NostrApps.com ⚪️ </a>
|
||||
</div>
|
||||
<!-- Nostr Links -->
|
||||
<button class="button" id="nostr-folder-btn" onclick="toggleFolder('folder1', 'nostr-folder-btn')">Use Nostr <span
|
||||
style="color: #F7931A;">←</span></button>
|
||||
<div class="links" id="folder1">
|
||||
|
||||
<!-- Cashu Links -->
|
||||
<button class="button" id="cashu-folder-btn" onclick="toggleFolder('folder3', 'cashu-folder-btn')">Use Cashu <span style="color: #F7931A;">←</span></button>
|
||||
<div class="links" id="folder3">
|
||||
<a href="https://cashu.btcforplebs.com" class="prefetch">Cashu Wallet Web App <span class="status-emoji" data-url="https://cashu.btcforplebs.com">⏳</span></a>
|
||||
<a href="https://nutstash.btcforplebs.com" class="prefetch">Nutstash Wallet Web App <span class="status-emoji" data-url="https://nutstash.btcforplebs.com">⏳</span></a>
|
||||
<a href="/mint" class="prefetch">Cashu Mint <span class="status-emoji" data-url="https://mint.btcforplebs.com">⏳</span></a>
|
||||
<a href="https://macadamia.cash" class="prefetch">Macadamia (iOS) ⚪️ </a>
|
||||
<a href="https://minibits.cash" class="prefetch">Minibits (Android) ⚪️ </a>
|
||||
</div>
|
||||
<a href="/relay" target="_blank" class="prefetch">Relay <span class="status-emoji"
|
||||
data-url="https://relay.btcforplebs.com">⏳</span></a>
|
||||
Nostr Tools
|
||||
<a href="https://nsec.btcforplebs.com" target="_blank" class="prefetch">Nsec Bunker <span class="status-emoji"
|
||||
data-url="https://nsec.btcforplebs.com">⏳</span></a>
|
||||
<a href="https://ncryptdesec.btcforplebs.com" target="_blank" class="prefetch">NcryptDEsec <span
|
||||
class="status-emoji" data-url="https://ncryptdesec.btcforplebs.com">⏳</span></a>
|
||||
|
||||
<a href="https://live.btcforplebs.com" class="button" class="prefetch">BTCforPlebs Live <span class="status-emoji" data-url="https://live.btcforplebs.com">⏳</span></a>
|
||||
<br></br>
|
||||
Nostr Clients
|
||||
<a href="https://nostrudel.btcforplebs.com" target="_blank" class="prefetch">Nostrudel <span class="status-emoji"
|
||||
data-url="https://nostrudel.btcforplebs.com">⏳</span></a>
|
||||
<a href="https://coracle.btcforplebs.com" target="_blank" class="prefetch">Coracle <span class="status-emoji"
|
||||
data-url="https://coracle.btcforplebs.com">⏳</span></a>
|
||||
<a href="https://jumble.btcforplebs.com" target="_blank" class="prefetch">Jumble <span class="status-emoji"
|
||||
data-url="https://jumble.btcforplebs.com">⏳</span></a>
|
||||
<a href="https://fevela.btcforplebs.com" target="_blank" class="prefetch">Fevela <span class="status-emoji"
|
||||
data-url="https://fevela.btcforplebs.com">⏳</span></a>
|
||||
<a href="https://nosotros.btcforplebs.com" target="_blank" class="prefetch">Nosotros <span class="status-emoji"
|
||||
data-url="https://nosotros.btcforplebs.com">⏳</span></a>
|
||||
<a href="https://shipyard.btcforplebs.com" target="_blank" class="prefetch">Shipyard <span class="status-emoji"
|
||||
data-url="https://shipyard.btcforplebs.com">⏳</span></a>
|
||||
|
||||
<small>🟢 = online | 🔴 = offline | ⚪️ = external site</small>
|
||||
<br></br>
|
||||
|
||||
Nostr Community (add community.btcforplebs.com)
|
||||
<a href="https://flotilla.btcforplebs.com/" target=" _blank" class="prefetch">Flotilla <span class="status-emoji"
|
||||
data-url="https://community.btcforplebs.com">⏳</span></a>
|
||||
|
||||
Google Drive Replacement (requires blossom server)
|
||||
<a href="https://bloom.btcforplebs.com" target="_blank" class="prefetch">Bloom <span class="status-emoji"
|
||||
data-url="https://bloom.btcforplebs.com">⏳</span></a>
|
||||
</div>
|
||||
|
||||
<!-- Cashu Links -->
|
||||
<button class="button" id="cashu-folder-btn" onclick="toggleFolder('folder3', 'cashu-folder-btn')">Use Cashu <span
|
||||
style="color: #F7931A;">←</span></button>
|
||||
<div class="links" id="folder3">
|
||||
<a href="https://cashu.btcforplebs.com" class="prefetch">Cashu Wallet Web App <span class="status-emoji"
|
||||
data-url="https://cashu.btcforplebs.com">⏳</span></a>
|
||||
<a href="/mint" class="prefetch">Cashu Mint <span class="status-emoji"
|
||||
data-url="https://mint.btcforplebs.com">⏳</span></a>
|
||||
<a href="https://macadamia.cash" class="prefetch">Macadamia (iOS) ⚪️ </a>
|
||||
<a href="https://minibits.cash" class="prefetch">Minibits (Android) ⚪️ </a>
|
||||
</div>
|
||||
|
||||
<small>🟢 = online | 🔴 = offline | ⚪️ = external site</small>
|
||||
<br></br>
|
||||
</div>
|
||||
|
||||
|
||||
<script src="https://btcforplebs.com/nostr-chat-widget.js"
|
||||
data-nostr-pubkey="75462f4dece4fbde54a535cfa09eb0d329bda090a9c2f9ed6b5f9d1d2fb6c15b"
|
||||
data-brand-name="Chat with BTCforPlebs"
|
||||
data-color="#fdad01"
|
||||
data-color-secondary="#222222">
|
||||
</script>
|
||||
|
||||
<script>
|
||||
// emoji status fetcher
|
||||
document.addEventListener('DOMContentLoaded', init);
|
||||
<script src="https://btcforplebs.com/nostr-chat-widget.js"
|
||||
data-nostr-pubkey="75462f4dece4fbde54a535cfa09eb0d329bda090a9c2f9ed6b5f9d1d2fb6c15b"
|
||||
data-brand-name="Chat with BTCforPlebs" data-color="#fdad01" data-color-secondary="#222222">
|
||||
</script>
|
||||
|
||||
const apiEndpoint = "/api/link-status";
|
||||
<script>
|
||||
// emoji status fetcher
|
||||
document.addEventListener('DOMContentLoaded', init);
|
||||
|
||||
async function init() {
|
||||
try {
|
||||
console.log("🔄 Fetching statuses from:", apiEndpoint);
|
||||
const response = await fetch(apiEndpoint);
|
||||
const statuses = await response.json();
|
||||
console.log("✅ Received statuses:", statuses);
|
||||
const apiEndpoint = "/api/link-status";
|
||||
|
||||
document.querySelectorAll(".status-emoji[data-url]").forEach(span => {
|
||||
const url = span.dataset.url;
|
||||
console.log("🔍 Checking span for:", url);
|
||||
async function init() {
|
||||
try {
|
||||
console.log("🔄 Fetching statuses from:", apiEndpoint);
|
||||
const response = await fetch(apiEndpoint);
|
||||
const statuses = await response.json();
|
||||
console.log("✅ Received statuses:", statuses);
|
||||
|
||||
if (!url) {
|
||||
console.warn("⚠️ Span missing data-url:", span);
|
||||
return;
|
||||
}
|
||||
document.querySelectorAll(".status-emoji[data-url]").forEach(span => {
|
||||
const url = span.dataset.url;
|
||||
console.log("🔍 Checking span for:", url);
|
||||
|
||||
const status = statuses[url];
|
||||
if (!url) {
|
||||
console.warn("⚠️ Span missing data-url:", span);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!status) {
|
||||
console.warn(`⚠️ No status found in JSON for: ${url}`);
|
||||
} else {
|
||||
const emoji = status === "online" ? "🟢" : "🔴";
|
||||
console.log(`✅ ${url} is ${status}, setting emoji: ${emoji}`);
|
||||
span.textContent = `${emoji} `;
|
||||
span.style.display = 'inline-block';
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("❌ Error fetching or processing statuses:", error);
|
||||
const status = statuses[url];
|
||||
|
||||
if (!status) {
|
||||
console.warn(`⚠️ No status found in JSON for: ${url}`);
|
||||
} else {
|
||||
const emoji = status === "online" ? "🟢" : "🔴";
|
||||
console.log(`✅ ${url} is ${status}, setting emoji: ${emoji}`);
|
||||
span.textContent = `${emoji} `;
|
||||
span.style.display = 'inline-block';
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("❌ Error fetching or processing statuses:", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</script>
|
||||
|
||||
<script>
|
||||
// Toggle folders
|
||||
function toggleFolder(folderId, buttonId) {
|
||||
const folder = document.getElementById(folderId);
|
||||
const button = document.getElementById(buttonId);
|
||||
if (!folder || !button) return;
|
||||
<script>
|
||||
// Toggle folders
|
||||
function toggleFolder(folderId, buttonId) {
|
||||
const folder = document.getElementById(folderId);
|
||||
const button = document.getElementById(buttonId);
|
||||
if (!folder || !button) return;
|
||||
|
||||
if (folder.style.display === "none" || folder.style.display === "") {
|
||||
if (folder.style.display === "none" || folder.style.display === "") {
|
||||
folder.style.display = "block";
|
||||
button.innerHTML = button.innerHTML.replace("←", "↓");
|
||||
|
||||
} else {
|
||||
|
||||
} else {
|
||||
folder.style.display = "none";
|
||||
button.innerHTML = button.innerHTML.replace("↓", "←");
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</script>
|
||||
|
||||
|
||||
<div id="footer"></div>
|
||||
<div id="footer"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
</html>
|
||||
|
||||
@@ -1,121 +1,113 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Cashu – BTCforPlebs</title>
|
||||
<meta name="description" content="Cashu is a Bitcoin third‑layer Chaumian ecash protocol. This guide covers how it works, its privacy features, and how to get started with app walkthroughs.">
|
||||
<meta name="description"
|
||||
content="Cashu is a Bitcoin third‑layer Chaumian ecash protocol. This guide covers how it works, its privacy features, and how to get started with app walkthroughs.">
|
||||
<link rel="icon" href="/images/favicon.png" type="image/png">
|
||||
<link rel="stylesheet" href="/assets/css/main.css">
|
||||
<link rel="apple-touch-icon" href="/images/apple-touch-icon.png">
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.6.0/css/all.min.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!-- Bitcoin Price Banner -->
|
||||
<div id="btc-price-banner">This page is under construction</div>
|
||||
|
||||
<div class="logo-container">
|
||||
<a href="/" class="prefetch">
|
||||
<img src="/images/btcforplebs_logo.gif" alt="Home" class="header-logo">
|
||||
</a>
|
||||
</div>
|
||||
<div class="container">
|
||||
<h1>Cashu – Chaumian Ecash on Bitcoin</h1>
|
||||
<p>Cashu is a lightweight, privacy‑preserving ecash system that runs on top of the Lightning Network and settles on‑chain. It uses zero‑knowledge proofs to hide the value of a transfer while remaining fully traceable when required.</p>
|
||||
|
||||
<!-- Dropdown navigation, similar to learn‑bitcoin -->
|
||||
<div class="dropdown">
|
||||
<select onchange="navigateToSection(this)">
|
||||
<option value="">Select a topic</option>
|
||||
<option value="what-is-cashu">What is Cashu?</option>
|
||||
<option value="privacy">Privacy & Zero‑Knowledge</option>
|
||||
<option value="technology">Underlying Technology</option>
|
||||
<option value="apps">Apps & Use‑Cases</option>
|
||||
<option value="getting-started">Getting Started</option>
|
||||
<option value="videos">Video Resources</option>
|
||||
</select>
|
||||
<div class="logo-container">
|
||||
<a href="/" class="prefetch">
|
||||
<img src="/images/btcforplebs_logo.gif" alt="Home" class="header-logo">
|
||||
</a>
|
||||
</div>
|
||||
<div class="container">
|
||||
<h1>Cashu – Chaumian Ecash on Bitcoin</h1>
|
||||
<p>Cashu is a lightweight, privacy‑preserving ecash system that runs on top of the Lightning Network and settles
|
||||
on‑chain. It uses zero‑knowledge proofs to hide the value of a transfer while remaining fully traceable when
|
||||
required.</p>
|
||||
|
||||
<!-- Sections -->
|
||||
<section id="what-is-cashu">
|
||||
<h2>What is Cashu?</h2>
|
||||
<p>Cashu is an ecash protocol that allows you to mint, transfer, and spend Bitcoin value without revealing the amount or the sender/receiver in public transactions. It leverages the Lightning Network for instant settlement while maintaining on‑chain traceability for regulatory compliance. Think of it as a privacy‑preserving UTXO that can be moved anywhere on the Lightning network.</p>
|
||||
</section>
|
||||
|
||||
<section id="privacy">
|
||||
<h2>Privacy & Zero‑Knowledge</h2>
|
||||
<p>At the core of Cashu is a zero‑knowledge range proof that proves the value of a note without exposing it. This means you can pay for goods or services with a Cashu note and the recipient can verify that the note contains sufficient value, while the transaction remains private on‑chain.</p>
|
||||
</section>
|
||||
|
||||
<section id="technology">
|
||||
<h2>Underlying Technology</h2>
|
||||
<p>Cashu uses a set of “notes” minted by a mint and signed with a unique key. Each note contains an amount and a unique identifier that the mint keeps encrypted. Transfers are executed with the Lightning Network, but minting and redeeming are done via on‑chain transactions that are fully verifiable.</p>
|
||||
</section>
|
||||
|
||||
<section id="apps">
|
||||
<h2>Apps & Use‑Cases</h2>
|
||||
<p>Several wallet applications support Cashu, from the <a href="https://cashu.me" target="_blank" class="prefetch">Cashu Web Wallet</a> to <a href="https://meltwallet.com" target="_blank" class="prefetch">Melt’s Lightning Wallet</a>. Below are quick links to some popular ones:</p>
|
||||
<div class="button-container">
|
||||
<a href="https://cashu.me" class="button" target="_blank" class="prefetch">Cashu Web Wallet</a>
|
||||
<a href="https://meltwallet.com" class="button" target="_blank" class="prefetch">Melt Wallet</a>
|
||||
<a href="https://github.com/cashu/cashu-js-demo" class="button" target="_blank" class="prefetch">Cashu JS Demo</a>
|
||||
<!-- Dropdown navigation, similar to learn‑bitcoin -->
|
||||
<div class="dropdown">
|
||||
<select onchange="navigateToSection(this)">
|
||||
<option value="">Select a topic</option>
|
||||
<option value="what-is-cashu">What is Cashu?</option>
|
||||
<option value="privacy">Privacy & Zero‑Knowledge</option>
|
||||
<option value="technology">Underlying Technology</option>
|
||||
<option value="apps">Cashu Apps</option>
|
||||
<option value="getting-started">Getting Started</option>
|
||||
</select>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="getting-started">
|
||||
<h2>Getting Started</h2>
|
||||
<p>To begin using Cashu, you’ll need a Lightning‑ready wallet that supports the protocol. Most of the wallets listed above have a <code>Cashu</code> switch or integration. Once installed, create a new Cashu account, mint a few notes, and start experimenting.</p>
|
||||
</section>
|
||||
<!-- Sections -->
|
||||
<section id="what-is-cashu">
|
||||
<h2>What is Cashu?</h2>
|
||||
<p>Cashu is an ecash protocol that allows you to mint, transfer, and spend sats without revealing
|
||||
the amount or the sender/receiver in public transactions. It leverages the Lightning Network for instant
|
||||
settlement on nodes. Think of it as a
|
||||
privacy‑preserving bearer instrument that can be minted and redeemed across on the Lightning network.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section id="videos">
|
||||
<h2>Video Resources</h2>
|
||||
<!-- Explainer Video 1 -->
|
||||
<h3>Explainer – What is Cashu?</h3>
|
||||
<div class="video-container">
|
||||
<video controls poster="https://i.imgur.com/Dc6m4zV.png" loading="lazy" style="width:100%">
|
||||
<source src="https://raw.githubusercontent.com/cashu/cashu-presentation/master/intro.mp4" type="video/mp4">
|
||||
Your browser does not support the video tag.
|
||||
</video>
|
||||
</div>
|
||||
<!-- Walkthrough Video 2 -->
|
||||
<h3>How to Mint a Cashu Note</h3>
|
||||
<div class="video-container">
|
||||
<video controls poster="https://i.imgur.com/Kq4c1rT.png" loading="lazy" style="width:100%">
|
||||
<source src="https://raw.githubusercontent.com/cashu/cashu-presentation/master/mint.mp4" type="video/mp4">
|
||||
Your browser does not support the video tag.
|
||||
</video>
|
||||
</div>
|
||||
<!-- App Walkthrough 3 -->
|
||||
<h3>Using Cashu in Melt Wallet</h3>
|
||||
<div class="video-container">
|
||||
<video controls poster="https://i.imgur.com/3p6zT9G.png" loading="lazy" style="width:100%">
|
||||
<source src="https://raw.githubusercontent.com/cashu/cashu-presentation/master/melt-demo.mp4" type="video/mp4">
|
||||
Your browser does not support the video tag.
|
||||
</video>
|
||||
</div>
|
||||
</section>
|
||||
<section id="privacy">
|
||||
<h2>Privacy & Zero‑Knowledge</h2>
|
||||
<p>At the core of Cashu is a zero‑knowledge range proof that proves the value of a note without exposing it.
|
||||
This means you can pay for goods or services with a Cashu note and the recipient can verify that the
|
||||
note contains sufficient value, while the transaction remains private on‑chain.</p>
|
||||
</section>
|
||||
|
||||
<div class="social-icons">
|
||||
<a href="https://github.com/cashu" target="_blank" class="prefetch"><i class="fa-brands fa-github"></i></a>
|
||||
<a href="https://twitter.com/CashuProtocol" target="_blank" class="prefetch"><i class="fa-brands fa-twitter"></i></a>
|
||||
<section id="technology">
|
||||
<h2>Underlying Technology</h2>
|
||||
<p>Cashu uses a set of “notes” minted by a mint and signed with a unique key. Each note contains an amount
|
||||
and a unique identifier that the mint keeps encrypted. Transfers are executed with the Lightning
|
||||
Network, and does require a mint to mint notes and therefore requires some level of trust in the mint.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section id="apps">
|
||||
<h2>Cashu Apps</h2>
|
||||
<p>Several wallet applications support Cashu. Below are quick links to some popular ones:</p>
|
||||
<div class="button-container">
|
||||
<a href="https://cashu.btcforplebs.com" class="button" target="_blank" class="prefetch">Cashu Wallet Web
|
||||
App</a>
|
||||
<a href="https://nutstash.btcforplebs.com" class="button" target="_blank" class="prefetch">Nutstash
|
||||
Wallet Web App</a>
|
||||
<a href="https://macadamia.cash" class="button" target="_blank" class="prefetch">Macadamia (iOS App)</a>
|
||||
<a href="https://minibits.cash" class="button" target="_blank" class="prefetch">Minibits (Android & iOS
|
||||
App)</a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="getting-started">
|
||||
<h2>Getting Started</h2>
|
||||
<p>To begin using Cashu, you’ll need a Lightning‑ready wallet that supports the protocol. Most of the
|
||||
wallets listed above have a <code>Cashu</code> switch or integration. Once installed, create a new Cashu
|
||||
account, mint a few notes, and start experimenting.</p>
|
||||
</section>
|
||||
|
||||
|
||||
<div class="social-icons">
|
||||
<a href="https://github.com/cashu" target="_blank" class="prefetch"><i class="fa-brands fa-github"></i></a>
|
||||
<a href="https://twitter.com/CashuProtocol" target="_blank" class="prefetch"><i
|
||||
class="fa-brands fa-twitter"></i></a>
|
||||
</div>
|
||||
|
||||
<div class="last-update">
|
||||
<small id="last-updated-text"></small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="last-update">
|
||||
<small id="last-updated-text"></small>
|
||||
</div>
|
||||
</div>
|
||||
<div id="footer"></div>
|
||||
<script src="/nostr-chat-widget.js"
|
||||
data-nostr-pubkey="75462f4dece4fbde54a535cfa09eb0d329bda090a9c2f9ed6b5f9d1d2fb6c15b"
|
||||
data-brand-name="Chat with BTCforPlebs"
|
||||
data-color="#fdad01"
|
||||
data-color-secondary="#222222">
|
||||
</script>
|
||||
<a href="#top" title="Back to Top">🔝</a>
|
||||
<div id="footer"></div>
|
||||
<script src="/nostr-chat-widget.js"
|
||||
data-nostr-pubkey="75462f4dece4fbde54a535cfa09eb0d329bda090a9c2f9ed6b5f9d1d2fb6c15b"
|
||||
data-brand-name="Chat with BTCforPlebs" data-color="#fdad01" data-color-secondary="#222222">
|
||||
</script>
|
||||
<a href="#top" title="Back to Top">🔝</a>
|
||||
<script>function navigateToSection(select) {
|
||||
const sectionId = select.value;
|
||||
if (sectionId) {
|
||||
document.getElementById(sectionId).scrollIntoView({ behavior: 'smooth' });
|
||||
}
|
||||
}</script>
|
||||
</html>
|
||||
const sectionId = select.value;
|
||||
if (sectionId) {
|
||||
document.getElementById(sectionId).scrollIntoView({ behavior: 'smooth' });
|
||||
}
|
||||
}</script>
|
||||
|
||||
</html>
|
||||
@@ -1,70 +1,113 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<title>Learn Nostr</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta property="og:image" content="/images/thumb.jpeg">
|
||||
<meta property="og:url" content="https://btcforplebs.com">
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<title>Learn Nostr</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta property="og:image" content="/images/thumb.jpeg">
|
||||
<meta property="og:url" content="https://btcforplebs.com">
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
|
||||
<!-- favicon -->
|
||||
<link rel="icon" href="/images/favicon.png" type="image/png">
|
||||
<!-- favicon -->
|
||||
<link rel="icon" href="/images/favicon.png" type="image/png">
|
||||
|
||||
<!-- scripts -->
|
||||
<script src="/assets/js/scripts.js" defer></script>
|
||||
<!-- scripts -->
|
||||
<script src="/assets/js/scripts.js" defer></script>
|
||||
|
||||
<link rel="stylesheet" href="/assets/css/main.css">
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.6.0/css/all.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="/assets/css/main.css">
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.6.0/css/all.min.css" rel="stylesheet">
|
||||
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<!-- Bitcoin Price Banner -->
|
||||
<div id="btc-price-banner">This page is under construction</div>
|
||||
|
||||
<div class="container">
|
||||
<div class="logo-container">
|
||||
<a href="/">
|
||||
<img src="/images/btcforplebs_logo.gif" alt="Home" class="header-logo">
|
||||
</a>
|
||||
<body>
|
||||
|
||||
<!-- Bitcoin Price Banner -->
|
||||
<div id="btc-price-banner">This page is under construction</div>
|
||||
|
||||
<div class="container">
|
||||
<div class="logo-container">
|
||||
<a href="/">
|
||||
<img src="/images/btcforplebs_logo.gif" alt="Home" class="header-logo">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<h1>Learn About Nostr</h1>
|
||||
<p>Nostr is a decentralized, censorship-resistant protocol for communication. Learn more about its potential by watching the video below.</p>
|
||||
<h2>What is Nostr and Why Should You Care?</h2>
|
||||
|
||||
<div class="video-container">
|
||||
<video controls playsinline width="100%" height="auto" poster="https://img.youtube.com/vi/8aZkg3OQc5c/maxresdefault.jpg">
|
||||
<source src="https://v.nostr.build/voby1u385n0ZwAbp.mp4" type="video/mp4">
|
||||
Your browser does not support the video tag.
|
||||
</video>
|
||||
</div>
|
||||
|
||||
<h1>Learn About Nostr</h1>
|
||||
<p>Nostr is a decentralized, censorship-resistant protocol for communication. Learn more about its potential by
|
||||
watching the video below.</p>
|
||||
<h2>What is Nostr and Why Should You Care?</h2>
|
||||
|
||||
<div class="video-container">
|
||||
<video controls playsinline width="100%" height="auto"
|
||||
poster="https://img.youtube.com/vi/8aZkg3OQc5c/maxresdefault.jpg">
|
||||
<source src="https://v.nostr.build/voby1u385n0ZwAbp.mp4" type="video/mp4">
|
||||
Your browser does not support the video tag.
|
||||
</video>
|
||||
</div>
|
||||
|
||||
<h2>Nostr Tools & Resources</h2>
|
||||
<p>Explore the ecosystem of Nostr apps and tools available on BTCforPlebs:</p>
|
||||
|
||||
<h3>Relay</h3>
|
||||
<ul>
|
||||
<li><a href="/relay" target="_blank"><strong>BTCforPlebs Relay</strong></a> – A reliable WebSocket relay for your
|
||||
Nostr clients. Connect at <code>wss://relay.btcforplebs.com</code>.</li>
|
||||
</ul>
|
||||
|
||||
<h3>Tools</h3>
|
||||
<ul>
|
||||
<li><a href="https://nsec.btcforplebs.com" target="_blank"><strong>Nsec Bunker</strong></a> – A remote signer that
|
||||
keeps your private keys safe while allowing you to use Nostr apps.</li>
|
||||
<li><a href="https://ncryptdesec.btcforplebs.com" target="_blank"><strong>NcryptDEsec</strong></a> – A tool for
|
||||
encrypting and decrypting Nostr keys using the NIP-49 standard.</li>
|
||||
</ul>
|
||||
|
||||
<h3>Clients</h3>
|
||||
<ul>
|
||||
<li><a href="https://nostrudel.btcforplebs.com" target="_blank"><strong>Nostrudel</strong></a> – A feature-rich
|
||||
web client for Nostr, offering a comprehensive social media experience.</li>
|
||||
<li><a href="https://coracle.btcforplebs.com" target="_blank"><strong>Coracle</strong></a> – A lightweight and
|
||||
highly customizable Nostr web client.</li>
|
||||
<li><a href="https://jumble.btcforplebs.com" target="_blank"><strong>Jumble</strong></a> – A unique Nostr client
|
||||
focused on specific user interactions.</li>
|
||||
<li><a href="https://fevela.btcforplebs.com" target="_blank"><strong>Fevela</strong></a> – A social client
|
||||
designed for ease of use and community engagement.</li>
|
||||
</ul>
|
||||
|
||||
<h3>Community</h3>
|
||||
<ul>
|
||||
<li><a href="https://flotilla.btcforplebs.com/" target="_blank"><strong>Flotilla</strong></a> – A
|
||||
community-focused platform built on Nostr for group discussions and collaboration.</li>
|
||||
</ul>
|
||||
|
||||
<h3>Storage</h3>
|
||||
<ul>
|
||||
<li><a href="https://bloom.btcforplebs.com" target="_blank"><strong>Bloom</strong></a> – A decentralized storage
|
||||
solution replacing Google Drive, powered by Blossom servers.</li>
|
||||
</ul>
|
||||
|
||||
<a href="/index.html" class="button">Home</a>
|
||||
|
||||
</div>
|
||||
<script src="/nostr-chat-widget.js"
|
||||
data-nostr-pubkey="75462f4dece4fbde54a535cfa09eb0d329bda090a9c2f9ed6b5f9d1d2fb6c15b"
|
||||
data-brand-name="Chat with BTCforPlebs"
|
||||
data-color="#fdad01"
|
||||
data-color-secondary="#222222">
|
||||
</script>
|
||||
</div>
|
||||
<script src="/nostr-chat-widget.js"
|
||||
data-nostr-pubkey="75462f4dece4fbde54a535cfa09eb0d329bda090a9c2f9ed6b5f9d1d2fb6c15b"
|
||||
data-brand-name="Chat with BTCforPlebs" data-color="#fdad01" data-color-secondary="#222222">
|
||||
</script>
|
||||
|
||||
<div id="footer"></div>
|
||||
|
||||
<a href="#top" title="Back to Top">🔝</a>
|
||||
<script>
|
||||
// --- 2. Dropdown navigation ---------------------------------------------
|
||||
window.navigateToSection = function (select) {
|
||||
const sectionId = select.value;
|
||||
if (sectionId) {
|
||||
const section = document.getElementById(sectionId);
|
||||
if (section) {
|
||||
section.scrollIntoView({ behavior: 'smooth' });
|
||||
<div id="footer"></div>
|
||||
|
||||
<a href="#top" title="Back to Top">🔝</a>
|
||||
<script>
|
||||
// --- 2. Dropdown navigation ---------------------------------------------
|
||||
window.navigateToSection = function (select) {
|
||||
const sectionId = select.value;
|
||||
if (sectionId) {
|
||||
const section = document.getElementById(sectionId);
|
||||
if (section) {
|
||||
section.scrollIntoView({ behavior: 'smooth' });
|
||||
}
|
||||
}
|
||||
}
|
||||
};</script>
|
||||
};</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
</html>
|
||||
File diff suppressed because one or more lines are too long
@@ -1,544 +0,0 @@
|
||||
/**
|
||||
* Nostr Chat Widget - Embeddable Version (Glassmorphism Design)
|
||||
*
|
||||
* EMBED IT WITH:
|
||||
* <script src="https://btcforplebs.com/nostr-chat-widget.js"
|
||||
* data-nostr-pubkey="YOUR_PUBKEY_HEX_FORMAT"
|
||||
* data-brand-name="My Company"
|
||||
* data-color="#8e30eb"
|
||||
* data-color-secondary="#ff8c00">
|
||||
* </script>
|
||||
*/
|
||||
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
// Get configuration from script tag
|
||||
const scriptTag = document.currentScript;
|
||||
const csPubkey = scriptTag.getAttribute('data-nostr-pubkey') || 'PUBKEY_TO_RECEICE_MESSAGES';
|
||||
const brandName = scriptTag.getAttribute('data-brand-name') || 'Support Team Messaging';
|
||||
const primaryColor = scriptTag.getAttribute('data-color') || '#fdad01';
|
||||
const secondaryColor = scriptTag.getAttribute('data-color-secondary') || '#000000';
|
||||
|
||||
// Default relay configuration
|
||||
const DEFAULT_RELAYS = [
|
||||
'wss://relay.damus.io',
|
||||
'wss://relay.primal.net',
|
||||
'wss://nos.lol',
|
||||
'wss://relay.btcforplebs.com'
|
||||
];
|
||||
|
||||
// Add viewport meta tag for mobile optimization
|
||||
let viewportMeta = document.querySelector('meta[name="viewport"]');
|
||||
if (!viewportMeta) {
|
||||
viewportMeta = document.createElement('meta');
|
||||
viewportMeta.name = 'viewport';
|
||||
viewportMeta.content = 'width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no';
|
||||
document.head.appendChild(viewportMeta);
|
||||
}
|
||||
|
||||
|
||||
// Inject custom styles with glassmorphism
|
||||
const style = document.createElement('style');
|
||||
style.textContent = `
|
||||
.safe-area-bottom {
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
}
|
||||
#nostr-chat-widget-root > div {
|
||||
pointer-events: auto !important;
|
||||
z-index: 99999 !important;
|
||||
}
|
||||
.glass-morphism {
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
backdrop-filter: blur(20px);
|
||||
-webkit-backdrop-filter: blur(20px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.15);
|
||||
}
|
||||
.glass-morphism-light {
|
||||
background: rgba(255, 255, 255, 0.03);
|
||||
backdrop-filter: blur(10px);
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
}
|
||||
.glass-input {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
backdrop-filter: blur(8px);
|
||||
-webkit-backdrop-filter: blur(8px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
.glass-input:focus {
|
||||
background: rgba(255, 255, 255, 0.25);
|
||||
border: 1px solid rgba(255, 255, 255, 0.4);
|
||||
}
|
||||
.glass-input::placeholder {
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
}
|
||||
@media (max-width: 640px) {
|
||||
#nostr-chat-widget-root .chat-window-mobile {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
bottom: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
max-height: 100vh !important;
|
||||
border-radius: 0 !important;
|
||||
}
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(style);
|
||||
|
||||
// Create widget container
|
||||
const widgetRoot = document.createElement('div');
|
||||
widgetRoot.id = 'nostr-chat-widget-root';
|
||||
document.body.appendChild(widgetRoot);
|
||||
|
||||
// Import map for nostr-tools
|
||||
const importMap = document.createElement('script');
|
||||
importMap.type = 'importmap';
|
||||
importMap.textContent = JSON.stringify({
|
||||
imports: {
|
||||
'nostr-tools': 'https://esm.sh/nostr-tools@1.17.0'
|
||||
}
|
||||
});
|
||||
document.head.appendChild(importMap);
|
||||
|
||||
// Main widget script
|
||||
const widgetScript = document.createElement('script');
|
||||
widgetScript.type = 'module';
|
||||
widgetScript.textContent = `
|
||||
import {
|
||||
relayInit,
|
||||
generatePrivateKey,
|
||||
getPublicKey,
|
||||
getEventHash,
|
||||
signEvent,
|
||||
nip19,
|
||||
nip04
|
||||
} from 'nostr-tools';
|
||||
|
||||
const CONFIG = {
|
||||
relays: ${JSON.stringify(DEFAULT_RELAYS)},
|
||||
csPubkey: '${csPubkey}',
|
||||
brandName: '${brandName}',
|
||||
primaryColor: '${primaryColor}',
|
||||
secondaryColor: '${secondaryColor}'
|
||||
};
|
||||
|
||||
let state = {
|
||||
isOpen: false,
|
||||
messages: [],
|
||||
inputMessage: '',
|
||||
myPrivKey: null,
|
||||
myPubKey: null,
|
||||
relays: [],
|
||||
connected: false,
|
||||
sessionId: null
|
||||
};
|
||||
|
||||
function getSessionKey() {
|
||||
const stored = localStorage.getItem('nostr_chat_session');
|
||||
if (stored) {
|
||||
try {
|
||||
const session = JSON.parse(stored);
|
||||
if (Date.now() - session.created < 24 * 60 * 60 * 1000) {
|
||||
return session.privKey;
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
const privKey = generatePrivateKey();
|
||||
localStorage.setItem('nostr_chat_session', JSON.stringify({
|
||||
privKey,
|
||||
created: Date.now()
|
||||
}));
|
||||
return privKey;
|
||||
}
|
||||
|
||||
async function init() {
|
||||
state.myPrivKey = getSessionKey();
|
||||
state.myPubKey = getPublicKey(state.myPrivKey);
|
||||
state.sessionId = state.myPubKey.substring(0, 8);
|
||||
|
||||
console.log('🔑 Session Identity:', nip19.npubEncode(state.myPubKey));
|
||||
|
||||
const relayPromises = CONFIG.relays.map(async (url) => {
|
||||
try {
|
||||
const relay = relayInit(url);
|
||||
|
||||
relay.on('connect', () => {
|
||||
console.log(\`✓ Connected to \${url}\`);
|
||||
checkConnection();
|
||||
});
|
||||
|
||||
relay.on('disconnect', () => {
|
||||
console.log(\`✗ Disconnected from \${url}\`);
|
||||
});
|
||||
|
||||
await relay.connect();
|
||||
return relay;
|
||||
} catch (error) {
|
||||
console.error(\`Failed: \${url}:\`, error);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
state.relays = (await Promise.all(relayPromises)).filter(r => r !== null);
|
||||
|
||||
if (state.relays.length === 0) {
|
||||
addMessage('system', '⚠️ Failed to connect to any relays');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(\`✓ Connected to \${state.relays.length}/\${CONFIG.relays.length} relays\`);
|
||||
|
||||
subscribeToReplies();
|
||||
loadPreviousMessages();
|
||||
|
||||
state.connected = true;
|
||||
render();
|
||||
}
|
||||
|
||||
function checkConnection() {
|
||||
const connected = state.relays.some(r => r.status === 1);
|
||||
state.connected = connected;
|
||||
render();
|
||||
}
|
||||
|
||||
function subscribeToReplies() {
|
||||
const filters = [{
|
||||
kinds: [4],
|
||||
'#p': [state.myPubKey],
|
||||
authors: [CONFIG.csPubkey],
|
||||
since: Math.floor(Date.now() / 1000) - 86400
|
||||
}];
|
||||
|
||||
console.log('🔔 Subscribing to replies...');
|
||||
|
||||
state.relays.forEach(relay => {
|
||||
const sub = relay.sub(filters);
|
||||
|
||||
sub.on('event', (event) => {
|
||||
handleIncomingMessage(event);
|
||||
});
|
||||
|
||||
sub.on('eose', () => {
|
||||
console.log(\`✓ Subscribed: \${relay.url}\`);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function loadPreviousMessages() {
|
||||
const stored = localStorage.getItem(\`nostr_chat_messages_\${state.sessionId}\`);
|
||||
if (stored) {
|
||||
try {
|
||||
const messages = JSON.parse(stored);
|
||||
messages.forEach(msg => state.messages.push(msg));
|
||||
render();
|
||||
} catch (e) {}
|
||||
}
|
||||
}
|
||||
|
||||
function saveMessages() {
|
||||
localStorage.setItem(\`nostr_chat_messages_\${state.sessionId}\`, JSON.stringify(state.messages));
|
||||
}
|
||||
|
||||
async function handleIncomingMessage(event) {
|
||||
try {
|
||||
if (state.messages.find(m => m.id === event.id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('📨 Received message');
|
||||
|
||||
const decryptedText = await nip04.decrypt(
|
||||
state.myPrivKey,
|
||||
event.pubkey,
|
||||
event.content
|
||||
);
|
||||
|
||||
const message = {
|
||||
id: event.id,
|
||||
text: decryptedText,
|
||||
sender: 'cs',
|
||||
timestamp: new Date(event.created_at * 1000).toISOString()
|
||||
};
|
||||
|
||||
addMessage('cs', decryptedText, message);
|
||||
|
||||
if (!document.hasFocus()) {
|
||||
const originalTitle = document.title;
|
||||
document.title = '💬 New message!';
|
||||
setTimeout(() => {
|
||||
document.title = originalTitle;
|
||||
}, 3000);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error decrypting message:', error);
|
||||
}
|
||||
}
|
||||
|
||||
async function sendMessage() {
|
||||
if (!state.inputMessage.trim()) return;
|
||||
|
||||
const messageText = state.inputMessage;
|
||||
state.inputMessage = '';
|
||||
|
||||
// Show message immediately (optimistic UI)
|
||||
const tempMessage = {
|
||||
id: 'temp_' + Date.now(),
|
||||
text: messageText,
|
||||
sender: 'user',
|
||||
timestamp: new Date().toISOString()
|
||||
};
|
||||
state.messages.push(tempMessage);
|
||||
render();
|
||||
scrollToBottom();
|
||||
|
||||
try {
|
||||
console.log('🔐 Encrypting and sending...');
|
||||
|
||||
const encrypted = await nip04.encrypt(
|
||||
state.myPrivKey,
|
||||
CONFIG.csPubkey,
|
||||
messageText
|
||||
);
|
||||
|
||||
let event = {
|
||||
kind: 4,
|
||||
created_at: Math.floor(Date.now() / 1000),
|
||||
tags: [['p', CONFIG.csPubkey]],
|
||||
content: encrypted,
|
||||
pubkey: state.myPubKey
|
||||
};
|
||||
|
||||
event.id = getEventHash(event);
|
||||
event.sig = signEvent(event, state.myPrivKey);
|
||||
|
||||
let published = 0;
|
||||
for (const relay of state.relays) {
|
||||
try {
|
||||
await relay.publish(event);
|
||||
published++;
|
||||
console.log(\`✓ Published to \${relay.url}\`);
|
||||
} catch (err) {
|
||||
console.error(\`✗ Failed: \${relay.url}:\`, err);
|
||||
}
|
||||
}
|
||||
|
||||
if (published === 0) {
|
||||
addMessage('system', '⚠️ Failed to send - no relay connections');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(\`✓ Published to \${published}/\${state.relays.length} relays\`);
|
||||
|
||||
// Update temp message with real ID
|
||||
const msgIndex = state.messages.findIndex(m => m.id === tempMessage.id);
|
||||
if (msgIndex !== -1) {
|
||||
state.messages[msgIndex].id = event.id;
|
||||
}
|
||||
saveMessages();
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error sending:', error);
|
||||
addMessage('system', '⚠️ Failed to send message');
|
||||
}
|
||||
}
|
||||
|
||||
function addMessage(sender, text, fullMessage = null) {
|
||||
const msg = fullMessage || {
|
||||
id: Date.now().toString(),
|
||||
text,
|
||||
sender,
|
||||
timestamp: new Date().toISOString()
|
||||
};
|
||||
|
||||
state.messages.push(msg);
|
||||
saveMessages();
|
||||
render();
|
||||
scrollToBottom();
|
||||
}
|
||||
|
||||
function scrollToBottom() {
|
||||
setTimeout(() => {
|
||||
const container = document.getElementById('nostr-messages');
|
||||
if (container) {
|
||||
container.scrollTop = container.scrollHeight;
|
||||
}
|
||||
}, 100);
|
||||
}
|
||||
|
||||
function escapeHtml(text) {
|
||||
const div = document.createElement('div');
|
||||
div.textContent = text;
|
||||
return div.innerHTML;
|
||||
}
|
||||
|
||||
function formatTime(timestamp) {
|
||||
const date = new Date(timestamp);
|
||||
return date.toLocaleTimeString('en-US', {
|
||||
hour: 'numeric',
|
||||
minute: '2-digit',
|
||||
hour12: true
|
||||
});
|
||||
}
|
||||
|
||||
function render() {
|
||||
const container = document.getElementById('nostr-chat-widget-root');
|
||||
|
||||
if (!container) return;
|
||||
|
||||
if (!state.isOpen) {
|
||||
container.innerHTML = \`
|
||||
<div class="fixed bottom-4 right-4 sm:bottom-6 sm:right-6 z-[99999]">
|
||||
<button onclick="window.NostrChat.open()"
|
||||
style="background: linear-gradient(to bottom right, \${CONFIG.primaryColor}, \${CONFIG.secondaryColor});"
|
||||
class="hover:opacity-90 text-white rounded-full p-4 sm:p-5 shadow-2xl transition-all transform hover:scale-110 active:scale-95"
|
||||
aria-label="Open chat"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="sm:w-7 sm:h-7">
|
||||
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
\`;
|
||||
return;
|
||||
}
|
||||
|
||||
container.innerHTML = \`
|
||||
<div class="fixed inset-0 sm:inset-auto sm:bottom-6 sm:right-6 z-[99999]">
|
||||
<div class="glass-morphism chat-window-mobile rounded-none sm:rounded-2xl shadow-2xl w-full h-full sm:w-96 sm:h-[600px] max-w-full flex flex-col overflow-hidden">
|
||||
</div>
|
||||
<div style="background: linear-gradient(to bottom right, ${CONFIG.primaryColor}, ${CONFIG.secondaryColor});" class="text-white py-4 px-5 sm:px-6 rounded-t-2xl">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center space-x-2">
|
||||
<img src="/assets/logo.svg" alt="logo" class="h-5 w-5 sm:h-6 sm:w-6 rounded" />
|
||||
<h3 class="font-semibold text-sm sm:text-base">${CONFIG.brandName}</h3>
|
||||
</div>
|
||||
<button onclick="window.NostrChat.close()" class="p-2 hover:bg-white/20 rounded-md transition-colors">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
<div style="background: linear-gradient(to bottom right, ${CONFIG.primaryColor}, ${CONFIG.secondaryColor});" class="text-white rounded-2xl rounded-tr-sm px-3 py-2 sm:px-4 sm:py-3 shadow-md text-sm sm:text-base">
|
||||
${escapeHtml(msg.text)}
|
||||
</div>
|
||||
<div class="text-center text-white/60 mt-8">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1" class="mx-auto mb-3 opacity-50">
|
||||
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path>
|
||||
</svg>
|
||||
<p class="text-sm">Start a conversation</p>
|
||||
</div>
|
||||
\` : state.messages.map(msg => {
|
||||
if (msg.sender === 'system') {
|
||||
return \`
|
||||
<div class="flex justify-center">
|
||||
<div class="bg-orange-100/40 backdrop-blur-sm text-orange-800 text-xs px-3 py-2 rounded-full border border-orange-300/50">
|
||||
\${escapeHtml(msg.text)}
|
||||
</div>
|
||||
</div>
|
||||
\`;
|
||||
} else if (msg.sender === 'user') {
|
||||
return \`
|
||||
<div class="flex justify-end">
|
||||
<div class="max-w-[85%] sm:max-w-xs">
|
||||
<div style="background: linear-gradient(to bottom right, \${CONFIG.primaryColor}, \${CONFIG.secondaryColor});" class="text-white rounded-2xl rounded-tr-sm px-3 py-2 sm:px-4 sm:py-3 shadow-md text-sm sm:text-base">
|
||||
\${escapeHtml(msg.text)}
|
||||
</div>
|
||||
<div class="text-xs text-white/60 mt-1 text-right">Sent \${formatTime(msg.timestamp)}</div>
|
||||
</div>
|
||||
</div>
|
||||
\`;
|
||||
} else if (msg.sender === 'cs') {
|
||||
return \`
|
||||
<div class="flex justify-start">
|
||||
<div class="max-w-[85%] sm:max-w-xs">
|
||||
<div style="background: linear-gradient(to bottom right, #9ca3af, #6b7280);" class="text-white rounded-2xl rounded-tl-sm px-3 py-2 sm:px-4 sm:py-3 shadow-md text-sm sm:text-base">
|
||||
\${escapeHtml(msg.text)}
|
||||
</div>
|
||||
<div class="text-xs text-white/60 mt-1">\${formatTime(msg.timestamp)}</div>
|
||||
</div>
|
||||
</div>
|
||||
\`;
|
||||
}
|
||||
return '';
|
||||
}).join('')}
|
||||
</div>
|
||||
|
||||
<div class="p-3 sm:p-3.5 mb-2 flex-shrink-0 safe-area-bottom">
|
||||
<div class="glass-input rounded-xl p-2 flex gap-2 items-center">
|
||||
<input
|
||||
id="nostr-message-input"
|
||||
type="text"
|
||||
value="\${escapeHtml(state.inputMessage)}"
|
||||
placeholder="Type your message..."
|
||||
class="flex-1 bg-transparent px-2 py-1.5 focus:outline-none text-sm sm:text-base text-white placeholder-white/60"
|
||||
\${!state.connected ? 'disabled' : ''}
|
||||
>
|
||||
<button
|
||||
onclick="window.NostrChat.send()"
|
||||
\${!state.connected || !state.inputMessage.trim() ? 'disabled' : ''}
|
||||
style="background: linear-gradient(to bottom right, \${CONFIG.primaryColor}, \${CONFIG.secondaryColor});"
|
||||
class="hover:opacity-90 disabled:opacity-40 text-white p-2 rounded-lg transition-all disabled:cursor-not-allowed active:scale-95 flex-shrink-0"
|
||||
aria-label="Send message"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<line x1="22" y1="2" x2="11" y2="13"></line>
|
||||
<polygon points="22 2 15 22 11 13 2 9 22 2"></polygon>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
\`;
|
||||
|
||||
const messageInput = document.getElementById('nostr-message-input');
|
||||
if (messageInput) {
|
||||
messageInput.addEventListener('input', (e) => {
|
||||
state.inputMessage = e.target.value;
|
||||
const sendButton = document.querySelector('button[onclick="window.NostrChat.send()"]');
|
||||
if (sendButton) {
|
||||
sendButton.disabled = !state.connected || !e.target.value.trim();
|
||||
}
|
||||
});
|
||||
messageInput.addEventListener('keypress', (e) => {
|
||||
if (e.key === 'Enter' && !e.shiftKey) {
|
||||
e.preventDefault();
|
||||
sendMessage();
|
||||
}
|
||||
});
|
||||
|
||||
const messagesContainer = document.getElementById('nostr-messages');
|
||||
if (messagesContainer && state.messages.length > 0) {
|
||||
setTimeout(() => {
|
||||
messagesContainer.scrollTop = messagesContainer.scrollHeight;
|
||||
}, 100);
|
||||
}
|
||||
|
||||
messageInput.focus();
|
||||
}
|
||||
}
|
||||
|
||||
// Expose global API
|
||||
window.NostrChat = {
|
||||
open: async () => {
|
||||
state.isOpen = true;
|
||||
render();
|
||||
if (state.relays.length === 0) {
|
||||
await init();
|
||||
}
|
||||
},
|
||||
close: () => {
|
||||
state.isOpen = false;
|
||||
render();
|
||||
},
|
||||
send: sendMessage
|
||||
};
|
||||
|
||||
// Initial render
|
||||
render();
|
||||
`;
|
||||
|
||||
document.body.appendChild(widgetScript);
|
||||
})();
|
||||
File diff suppressed because one or more lines are too long
@@ -3,21 +3,23 @@
|
||||
<script src="/assets/js/scripts.js"></script>
|
||||
|
||||
|
||||
<style>
|
||||
.nostr-icon {
|
||||
width: 1em; /* same size as the other icons */
|
||||
height: 1em;
|
||||
background-color: currentColor; /* inherits the surrounding text colour */
|
||||
<style>
|
||||
.nostr-icon {
|
||||
width: 1em;
|
||||
/* same size as the other icons */
|
||||
height: 1em;
|
||||
background-color: currentColor;
|
||||
/* inherits the surrounding text colour */
|
||||
|
||||
/* mask the PNG – requires a PNG with a transparent background */
|
||||
mask: url("https://i.nostr.build/rEzdEtwDi8VDpQiX.png") no-repeat center / contain;
|
||||
-webkit-mask: url("https://i.nostr.build/rEzdEtwDi8VDpQiX.png") no-repeat center / contain;
|
||||
}
|
||||
</style>
|
||||
/* mask the PNG – requires a PNG with a transparent background */
|
||||
mask: url("https://i.nostr.build/rEzdEtwDi8VDpQiX.png") no-repeat center / contain;
|
||||
-webkit-mask: url("https://i.nostr.build/rEzdEtwDi8VDpQiX.png") no-repeat center / contain;
|
||||
}
|
||||
</style>
|
||||
|
||||
<h3>We operate on solely on Bitcoin donations; from a pleb just like you!</h3>
|
||||
|
||||
<!-- Button Section -->
|
||||
<!-- Button Section -->
|
||||
<button class="qr-button" id="onChainButton">Donate with On-Chain</button>
|
||||
<button class="qr-button" id="lightningButton">Donate Via Lightning</button>
|
||||
<div id="qrCodes" style="display: none;">
|
||||
@@ -27,32 +29,36 @@
|
||||
</div>
|
||||
<div id="lightningQRCode" style="display: none;">
|
||||
<img src="/images/lightning-donate.png" alt="Lightning QR Code"><br>
|
||||
<h3>btcforplebs@ln.pl3bs.co</h3>
|
||||
<h3>btcforplebs@lightning.btcforplebs.com</h3>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Social Links Section -->
|
||||
<!-- Social Links Section -->
|
||||
<div class="social-icons">
|
||||
<a href="https://nostrudel.ninja/#/u/npub1w4rz7n0vunaau499xh86p84s6v5mmgys48p0nmttt7w36takc9dsf4382j" target="_blank" title="Nostr" class="prefetch">
|
||||
<a href="https://nostrudel.btcforplebs.com/#/u/npub1w4rz7n0vunaau499xh86p84s6v5mmgys48p0nmttt7w36takc9dsf4382j"
|
||||
target="_blank" title="Nostr" class="prefetch">
|
||||
<div class="nostr-icon"></div>
|
||||
<a href="https://x.com/btcforplebs" target="_blank" title="X" class="prefetch">
|
||||
<i class="fa-brands fa-twitter"></i>
|
||||
</a>
|
||||
<a href="https://youtube.com/@btcforplebs" target="_blank" title="YouTube" class="prefetch">
|
||||
<i class="fa-brands fa-youtube"></i>
|
||||
<a href="https://twitch.tv/btcforplebs" target="_blank" title="Twitch" class="prefetch">
|
||||
<i class="fa-brands fa-twitch"></i>
|
||||
</a>
|
||||
<a href="btcforplebs@gmail.com" target="_blank" title="Email" class="prefetch">
|
||||
<i class="fa-solid fa-envelope"></i>
|
||||
<a href="https://github.com/btcforplebs/BTCforPlebs.com" target="_blank" title="GitHub" class="prefetch">
|
||||
<i class="fa-solid fa-github"></i>
|
||||
</a>
|
||||
<a href="https://x.com/btcforplebs" target="_blank" title="X" class="prefetch">
|
||||
<i class="fa-brands fa-twitter"></i>
|
||||
</a>
|
||||
<a href="https://youtube.com/@btcforplebs" target="_blank" title="YouTube" class="prefetch">
|
||||
<i class="fa-brands fa-youtube"></i>
|
||||
<a href="https://twitch.tv/btcforplebs" target="_blank" title="Twitch" class="prefetch">
|
||||
<i class="fa-brands fa-twitch"></i>
|
||||
</a>
|
||||
<a href="mailto:btcforplebs@gmail.com" target="_blank" title="Email" class="prefetch">
|
||||
<i class="fa-solid fa-envelope"></i>
|
||||
<a href="https://gitea.btcforplebs.com/btcforplebs/BTCforPlebs.com" target="_blank" title="GitHub"
|
||||
class="prefetch">
|
||||
<i class="fa-brands fa-github"></i>
|
||||
</a>
|
||||
</div>
|
||||
<!-- Last Update Section -->
|
||||
<!-- Last Update Section -->
|
||||
<div class="last-update">
|
||||
<small id="last-updated-text"></small>
|
||||
</div>
|
||||
|
||||
<br></br>
|
||||
<small>This website is a work in progress and always under development. None of the content on this website is Financial Advice. <br>Do Your Own Research (DYOR) before converting your worthless Fiat Money into the Hardest Money ever known to mankind.<br> - Stack Sats <br> - HODL your keys<br> - It's not that complicated</small>
|
||||
</footer>
|
||||
<small>This website is a work in progress and always under development. None of the content on this website is
|
||||
Financial Advice. <br>Do Your Own Research (DYOR) before converting your worthless Fiat Money into the Hardest
|
||||
Money ever known to mankind.<br> - Stack Sats <br> - HODL your keys<br> - It's not that complicated</small>
|
||||
</footer>
|
||||
58
public/test-widget-v2.html
Normal file
58
public/test-widget-v2.html
Normal file
@@ -0,0 +1,58 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Nostr Chat Widget V2 Test</title>
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||||
background: linear-gradient(45deg, #1a1a1a, #2d2d2d);
|
||||
min-height: 100vh;
|
||||
color: white;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.content {
|
||||
text-align: center;
|
||||
padding: 2rem;
|
||||
max-width: 600px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.5rem;
|
||||
margin-bottom: 1rem;
|
||||
background: linear-gradient(to right, #fdad01, #ff8c00);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
|
||||
p {
|
||||
color: #aaa;
|
||||
line-height: 1.6;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="content">
|
||||
<h1>Nostr Chat Widget V2</h1>
|
||||
<p>This is a test page for the refactored Nostr Chat Widget.</p>
|
||||
<p>The widget should appear in the bottom right corner.</p>
|
||||
</div>
|
||||
|
||||
<!-- Widget Embed Code -->
|
||||
<!-- Using a random hex pubkey for testing purposes -->
|
||||
<script src="nostr-chat-widget.js"
|
||||
data-nostr-pubkey="75462f4dece4fbde54a535cfa09eb0d329bda090a9c2f9ed6b5f9d1d2fb6c15b"
|
||||
data-brand-name="Test Widget" data-color="#fdad01" data-color-secondary="#ff8c00">
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
Reference in New Issue
Block a user