diff --git a/app.js b/app.js index 4b51de8..877ef26 100644 --- a/app.js +++ b/app.js @@ -138,6 +138,7 @@ async function init() { updateLoad(95); setupControls(); + initVisitorIdentity(); window.addEventListener('resize', onResize); debugOverlay = document.getElementById('debug-overlay'); @@ -1085,12 +1086,93 @@ function addChatMessage(type, text) { const container = document.getElementById('chat-messages'); const div = document.createElement('div'); div.className = `chat-msg chat-msg-${type}`; - const prefixes = { user: '[ALEXANDER]', timmy: '[TIMMY]', system: '[NEXUS]', error: '[ERROR]' }; + let userPrefix = '[GUEST]'; + if (type === 'user' && visitorPubkey) { + const short = visitorPubkey.slice(0, 8) + '…' + visitorPubkey.slice(-4); + userPrefix = `[${short}]`; + } else if (type === 'user') { + userPrefix = '[GUEST]'; + } + const prefixes = { user: userPrefix, timmy: '[TIMMY]', system: '[NEXUS]', error: '[ERROR]' }; div.innerHTML = `${prefixes[type] || '[???]'} ${text}`; container.appendChild(div); container.scrollTop = container.scrollHeight; } +// ═══ NIP-07 VISITOR IDENTITY ═══ +let visitorPubkey = null; +const VISITOR_STORAGE_KEY = 'nexus_visitor_pubkey'; + +function initVisitorIdentity() { + const cached = localStorage.getItem(VISITOR_STORAGE_KEY); + if (cached) { + visitorPubkey = cached; + updateVisitorUI(true); + addChatMessage('system', `Welcome back, ${abbrPubkey(cached)}. Identity recognized.`); + } + + // Show connect button only if NIP-07 extension detected and not already connected + if (window.nostr && !visitorPubkey) { + document.getElementById('nostr-connect-btn').style.display = 'block'; + } + + document.getElementById('nostr-connect-btn').addEventListener('click', connectNostrIdentity); + document.getElementById('nostr-disconnect-btn').addEventListener('click', disconnectNostrIdentity); +} + +async function connectNostrIdentity() { + if (!window.nostr) return; + const btn = document.getElementById('nostr-connect-btn'); + btn.textContent = '⚡ CONNECTING…'; + btn.disabled = true; + try { + const pubkey = await window.nostr.getPublicKey(); + if (!pubkey) throw new Error('No pubkey returned'); + visitorPubkey = pubkey; + localStorage.setItem(VISITOR_STORAGE_KEY, pubkey); + updateVisitorUI(true); + addChatMessage('system', `Identity linked: ${abbrPubkey(pubkey)}. Timmy remembers you.`); + } catch (e) { + btn.textContent = '⚡ CONNECT IDENTITY'; + btn.disabled = false; + addChatMessage('error', 'Identity connection failed. Extension declined or unavailable.'); + } +} + +function disconnectNostrIdentity() { + visitorPubkey = null; + localStorage.removeItem(VISITOR_STORAGE_KEY); + updateVisitorUI(false); + addChatMessage('system', 'Identity disconnected. Returning to guest mode.'); + if (window.nostr) { + document.getElementById('nostr-connect-btn').style.display = 'block'; + } +} + +function updateVisitorUI(connected) { + const indicator = document.getElementById('visitor-indicator'); + const nameEl = document.getElementById('visitor-name'); + const connectBtn = document.getElementById('nostr-connect-btn'); + const disconnectBtn = document.getElementById('nostr-disconnect-btn'); + + if (connected && visitorPubkey) { + indicator.className = 'visitor-indicator connected'; + nameEl.className = 'connected'; + nameEl.textContent = abbrPubkey(visitorPubkey); + connectBtn.style.display = 'none'; + disconnectBtn.style.display = 'block'; + } else { + indicator.className = 'visitor-indicator'; + nameEl.className = ''; + nameEl.textContent = 'GUEST'; + disconnectBtn.style.display = 'none'; + } +} + +function abbrPubkey(pubkey) { + return pubkey.slice(0, 8) + '…' + pubkey.slice(-4); +} + // ═══ PORTAL INTERACTION ═══ function checkPortalProximity() { if (portalOverlayActive) return; diff --git a/index.html b/index.html index dd4d42d..d1b0830 100644 --- a/index.html +++ b/index.html @@ -80,6 +80,17 @@
+ +