sxclij on Nostr: <!DOCTYPE html> <html> <head> <title>Nostr Minimal Client</title> <meta ...
<!DOCTYPE html>
<html>
<head>
<title>Nostr Minimal Client</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
body { font-family: system-ui; max-width: 600px; margin: 0 auto; padding: 20px; }
textarea, input, button { width: 100%; margin: 10px 0; padding: 10px; box-sizing: border-box; }
#posts { margin-top: 20px; }
.post { border: 1px solid #ccc; padding: 10px; margin: 10px 0; }
</style>
</head>
<body>
<textarea id="privkey" placeholder="Private key (hex)"></textarea>
<button onclick="generateKeys()">Generate Keys</button>
<input type="text" id="content" placeholder="Note content">
<button onclick="publishNote()">Publish Note</button>
<div id="posts"></div>
<script>
// Nostr Core Functionality
const Nostr = {
generatePrivateKey: async () => {
const array = new Uint8Array(32);
window.crypto.getRandomValues(array);
return Array.from(array, byte => byte.toString(16).padStart(2, '0')).join('');
},
getPublicKey: async (privkey) => {
const key = await crypto.subtle.importKey(
'raw',
new TextEncoder().encode(privkey),
{ name: 'HMAC' },
false,
['sign']
);
const pubkey = await crypto.subtle.sign('HMAC', key, new Uint8Array());
return Array.from(new Uint8Array(pubkey), byte =>
byte.toString(16).padStart(2, '0')).join('').slice(0, 64);
},
signEvent: async (event, privkey) => {
const encoder = new TextEncoder();
const data = encoder.encode(JSON.stringify(event));
const key = await crypto.subtle.importKey(
'raw',
encoder.encode(privkey),
{ name: 'HMAC', hash: 'SHA-256' },
false,
['sign']
);
const sig = await crypto.subtle.sign('HMAC', key, data);
return Array.from(new Uint8Array(sig), byte =>
byte.toString(16).padStart(2, '0')).join('');
}
};
// Application Logic
let relay = null;
async function generateKeys() {
const privkey = await Nostr.generatePrivateKey();
document.getElementById('privkey').value = privkey;
}
async function publishNote() {
const privkey = document.getElementById('privkey').value;
const content = document.getElementById('content').value;
const event = {
kind: 1,
created_at: Math.floor(Date.now() / 1000),
tags: [],
content: content
};
event.pubkey = await Nostr.getPublicKey(privkey);
event.id = await Nostr.signEvent(event, privkey);
event.sig = await Nostr.signEvent(event, privkey);
relay.send(JSON.stringify(['EVENT', event]));
}
// Relay Connection
function connectRelay() {
relay = new WebSocket('wss://relay.damus.io');
relay.onmessage = (e) => {
const [type, , event] = JSON.parse(e.data);
if (type === 'EVENT' && event.kind === 1) {
const post = document.createElement('div');
post.className = 'post';
post.innerHTML = `<strong>${event.pubkey.slice(0, 8)}:</strong> ${event.content}`;
document.getElementById('posts').prepend(post);
}
};
relay.onopen = () => {
relay.send(JSON.stringify(['REQ', 'minimal-client', {kinds: [1]}]));
};
}
// Initialize
connectRelay();
</script>
</body>
</html>
<html>
<head>
<title>Nostr Minimal Client</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
body { font-family: system-ui; max-width: 600px; margin: 0 auto; padding: 20px; }
textarea, input, button { width: 100%; margin: 10px 0; padding: 10px; box-sizing: border-box; }
#posts { margin-top: 20px; }
.post { border: 1px solid #ccc; padding: 10px; margin: 10px 0; }
</style>
</head>
<body>
<textarea id="privkey" placeholder="Private key (hex)"></textarea>
<button onclick="generateKeys()">Generate Keys</button>
<input type="text" id="content" placeholder="Note content">
<button onclick="publishNote()">Publish Note</button>
<div id="posts"></div>
<script>
// Nostr Core Functionality
const Nostr = {
generatePrivateKey: async () => {
const array = new Uint8Array(32);
window.crypto.getRandomValues(array);
return Array.from(array, byte => byte.toString(16).padStart(2, '0')).join('');
},
getPublicKey: async (privkey) => {
const key = await crypto.subtle.importKey(
'raw',
new TextEncoder().encode(privkey),
{ name: 'HMAC' },
false,
['sign']
);
const pubkey = await crypto.subtle.sign('HMAC', key, new Uint8Array());
return Array.from(new Uint8Array(pubkey), byte =>
byte.toString(16).padStart(2, '0')).join('').slice(0, 64);
},
signEvent: async (event, privkey) => {
const encoder = new TextEncoder();
const data = encoder.encode(JSON.stringify(event));
const key = await crypto.subtle.importKey(
'raw',
encoder.encode(privkey),
{ name: 'HMAC', hash: 'SHA-256' },
false,
['sign']
);
const sig = await crypto.subtle.sign('HMAC', key, data);
return Array.from(new Uint8Array(sig), byte =>
byte.toString(16).padStart(2, '0')).join('');
}
};
// Application Logic
let relay = null;
async function generateKeys() {
const privkey = await Nostr.generatePrivateKey();
document.getElementById('privkey').value = privkey;
}
async function publishNote() {
const privkey = document.getElementById('privkey').value;
const content = document.getElementById('content').value;
const event = {
kind: 1,
created_at: Math.floor(Date.now() / 1000),
tags: [],
content: content
};
event.pubkey = await Nostr.getPublicKey(privkey);
event.id = await Nostr.signEvent(event, privkey);
event.sig = await Nostr.signEvent(event, privkey);
relay.send(JSON.stringify(['EVENT', event]));
}
// Relay Connection
function connectRelay() {
relay = new WebSocket('wss://relay.damus.io');
relay.onmessage = (e) => {
const [type, , event] = JSON.parse(e.data);
if (type === 'EVENT' && event.kind === 1) {
const post = document.createElement('div');
post.className = 'post';
post.innerHTML = `<strong>${event.pubkey.slice(0, 8)}:</strong> ${event.content}`;
document.getElementById('posts').prepend(post);
}
};
relay.onopen = () => {
relay.send(JSON.stringify(['REQ', 'minimal-client', {kinds: [1]}]));
};
}
// Initialize
connectRelay();
</script>
</body>
</html>