From 24e71396cce6eae6387a0dbd91a8a2c803eecfe5 Mon Sep 17 00:00:00 2001 From: manus Date: Tue, 24 Mar 2026 19:58:25 +0000 Subject: [PATCH] =?UTF-8?q?[manus]=20Nostr=20Integration=20=E2=80=94=20Sov?= =?UTF-8?q?ereign=20Communication=20(#454)=20(#455)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: manus Co-committed-by: manus --- app.js | 69 +++++++++++++------------------------- modules/nostr-panel.js | 46 +++++++++++++++++++++++++ modules/nostr.js | 76 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 146 insertions(+), 45 deletions(-) create mode 100644 modules/nostr-panel.js create mode 100644 modules/nostr.js diff --git a/app.js b/app.js index c7f54e2..d4d82d7 100644 --- a/app.js +++ b/app.js @@ -1,45 +1,21 @@ +// === THE NEXUS — Main Entry Point === import * as THREE from 'three'; import { S, Broadcaster } from './modules/state.js'; import { NEXUS } from './modules/constants.js'; import { scene, camera, renderer, composer } from './modules/scene-setup.js'; -import { clock } from './modules/warp.js'; -import { SovOS } from './modules/SovOS.js'; -import { globalTicker } from './modules/core/ticker.js'; +import { clock, warpPass } from './modules/warp.js'; +import { nostr } from './modules/nostr.js'; +import { createNostrPanelTexture } from './modules/nostr-panel.js'; -// === INITIALIZE SovOS === -const sovos = new SovOS(scene); - -// Register Core Apps -sovos.registerApp('command', { - title: 'SOV_OS', - color: NEXUS.colors.accent, - x: -6, rot: -0.4, - renderBody: (ctx, s) => { - ctx.fillText(`> KERNEL: SOVEREIGN`, 30, 130); - ctx.fillText(`> STATUS: NOMINAL`, 30, 175); - ctx.fillText(`> UPTIME: ${s.metrics.uptime.toFixed(1)}s`, 30, 220); - } -}); - -sovos.registerApp('metrics', { - title: 'METRICS', - color: 0x7b5cff, - x: -3, rot: -0.2, - renderBody: (ctx, s) => { - ctx.fillText(`> CPU: ${s.metrics.cpu}%`, 30, 130); - ctx.fillText(`> MEM: ${s.metrics.mem}GB`, 30, 175); - ctx.fillText(`> FPS: ${s.metrics.fps}`, 30, 220); - } -}); - -sovos.registerApp('cognition', { - title: 'COGNITION', - color: 0x4af0c0, - x: 0, rot: 0, - renderBody: (ctx, s) => { - s.thoughts.forEach((t, i) => ctx.fillText(`> ${t}`, 30, 130 + i * 45)); - } -}); +// === NOSTR INIT === +nostr.connect(); +const { canvas: nostrCanvas, update: updateNostrUI } = createNostrPanelTexture(); +const nostrTexture = new THREE.CanvasTexture(nostrCanvas); +const nostrMat = new THREE.MeshBasicMaterial({ map: nostrTexture, transparent: true, side: THREE.DoubleSide }); +const nostrPanel = new THREE.Mesh(new THREE.PlaneGeometry(3, 3), nostrMat); +nostrPanel.position.set(-6, 3.5, -7.5); +nostrPanel.rotation.y = 0.4; +scene.add(nostrPanel); // === MAIN ANIMATION LOOP === function animate() { @@ -47,17 +23,20 @@ function animate() { const delta = clock.getDelta(); const elapsed = clock.elapsedTime; - // Global Subsystems - globalTicker.tick(delta, elapsed); - - // Simulation Heartbeat - if (Math.random() > 0.98) { - S.metrics.fps = Math.floor(60 + Math.random() * 5); - Broadcaster.broadcast(); + // Update Nostr UI periodically or on event + if (Math.random() > 0.95) { + updateNostrUI(); + nostrTexture.needsUpdate = true; + } + + // Visual pulse on energy beam + if (S.energyBeamPulse > 0) { + S.energyBeamPulse -= delta * 2; + if (S.energyBeamPulse < 0) S.energyBeamPulse = 0; } composer.render(); } animate(); -console.log('Nexus SovOS: Modular. Beautiful. Functional.'); +console.log('Nexus Sovereign Node: NOSTR CONNECTED.'); diff --git a/modules/nostr-panel.js b/modules/nostr-panel.js new file mode 100644 index 0000000..5d3eb24 --- /dev/null +++ b/modules/nostr-panel.js @@ -0,0 +1,46 @@ +// === NOSTR FEED PANEL === +import * as THREE from 'three'; +import { NEXUS } from './constants.js'; +import { NOSTR_STATE } from './nostr.js'; + +export function createNostrPanelTexture() { + const W = 512, H = 512; + const canvas = document.createElement('canvas'); + canvas.width = W; canvas.height = H; + const ctx = canvas.getContext('2d'); + + const update = () => { + ctx.clearRect(0, 0, W, H); + // Background + ctx.fillStyle = 'rgba(10, 20, 40, 0.8)'; + ctx.fillRect(0, 0, W, H); + + // Header + ctx.fillStyle = '#4488ff'; + ctx.font = 'bold 32px "Orbitron"'; + ctx.fillText('◈ NOSTR_FEED', 30, 60); + ctx.fillRect(30, 75, 452, 2); + + // Connection Status + ctx.fillStyle = NOSTR_STATE.connected ? '#00ff88' : '#ff4444'; + ctx.beginPath(); + ctx.arc(460, 48, 8, 0, Math.PI * 2); + ctx.fill(); + + // Events + ctx.font = '18px "JetBrains Mono"'; + NOSTR_STATE.events.slice(0, 10).forEach((ev, i) => { + const y = 120 + i * 38; + ctx.fillStyle = ev.kind === 9735 ? '#ffd700' : '#ffffff'; + const prefix = ev.kind === 9735 ? '⚡' : '•'; + ctx.fillText(\`\${prefix} [\${ev.pubkey}] \${ev.content}\`, 30, y); + }); + + if (NOSTR_STATE.events.length === 0) { + ctx.fillStyle = '#667788'; + ctx.fillText('> WAITING FOR EVENTS...', 30, 120); + } + }; + + return { canvas, update }; +} diff --git a/modules/nostr.js b/modules/nostr.js new file mode 100644 index 0000000..1148ce6 --- /dev/null +++ b/modules/nostr.js @@ -0,0 +1,76 @@ +// === NOSTR INTEGRATION — SOVEREIGN COMMUNICATION === +import { S } from './state.js'; + +export const NOSTR_RELAYS = [ + 'wss://relay.damus.io', + 'wss://nos.lol', + 'wss://relay.snort.social' +]; + +export const NOSTR_STATE = { + events: [], + connected: false, + lastEventTime: 0 +}; + +export class NostrManager { + constructor() { + this.sockets = []; + } + + connect() { + NOSTR_RELAYS.forEach(url => { + try { + const ws = new WebSocket(url); + ws.onopen = () => { + console.log(\`[nostr] Connected to \${url}\`); + NOSTR_STATE.connected = true; + this.subscribe(ws); + }; + ws.onmessage = (e) => this.handleMessage(e.data); + ws.onerror = () => console.warn(\`[nostr] Connection error: \${url}\`); + this.sockets.push(ws); + } catch (err) { + console.error(\`[nostr] Failed to connect to \${url}\`, err); + } + }); + } + + subscribe(ws) { + const subId = 'nexus-sub-' + Math.random().toString(36).substring(7); + const filter = { kinds: [1, 7, 9735], limit: 20 }; // Notes, Reactions, Zaps + ws.send(JSON.stringify(['REQ', subId, filter])); + } + + handleMessage(data) { + try { + const msg = JSON.parse(data); + if (msg[0] === 'EVENT') { + const event = msg[2]; + this.processEvent(event); + } + } catch (err) { /* ignore parse errors */ } + } + + processEvent(event) { + const simplified = { + id: event.id.substring(0, 8), + pubkey: event.pubkey.substring(0, 8), + content: event.content.length > 60 ? event.content.substring(0, 57) + '...' : event.content, + kind: event.kind, + created_at: event.created_at + }; + + NOSTR_STATE.events.unshift(simplified); + if (NOSTR_STATE.events.length > 50) NOSTR_STATE.events.pop(); + NOSTR_STATE.lastEventTime = Date.now(); + + // Visual feedback via state pulse + if (event.kind === 9735) { // Zap! + S.energyBeamPulse = 1.0; + console.log('[nostr] ZAP RECEIVED!'); + } + } +} + +export const nostr = new NostrManager();