From 937c2c7dc39fb1a5adfd3c5919b648f245406e67 Mon Sep 17 00:00:00 2001 From: Alexander Whitestone Date: Tue, 24 Mar 2026 01:05:28 -0400 Subject: [PATCH] feat: northern lights respond to git push/merge events (#261) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add aurora borealis ShaderMaterial (layered sine-wave curtains, green→cyan→violet colour cycling, additive blending) positioned high in the background sky - Aurora animates continuously; uFlashIntensity uniform decays each frame so flashes fade naturally - On git push: triggerAuroraPushFlash() sets intensity to 0.5 - On PR merge: triggerMergeFlash() surges aurora to full 1.0 intensity in addition to existing star/constellation flash - Add push-notification event routing to ws-client.js (types 'push' and 'push-notification') - Poll Gitea commits API every 30 s as fallback when WebSocket is unavailable; detects new commits and triggers push flash Fixes #261 --- app.js | 126 ++++++++++++++++++++++++++++++++++++++++++++++++++- ws-client.js | 5 ++ 2 files changed, 130 insertions(+), 1 deletion(-) diff --git a/app.js b/app.js index 540375d..8a29085 100644 --- a/app.js +++ b/app.js @@ -473,6 +473,79 @@ const clouds = new THREE.Mesh(cloudGeometry, cloudMaterial); clouds.position.y = CLOUD_LAYER_Y; scene.add(clouds); +// === NORTHERN LIGHTS (AURORA BOREALIS) === +// Animated aurora in the background sky. +// Responds to git push events (medium flash) and merge events (bright flash). + +const AuroraShader = { + uniforms: { + uTime: { value: 0.0 }, + uFlashIntensity: { value: 0.0 }, + }, + vertexShader: ` + varying vec2 vUv; + void main() { + vUv = uv; + gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); + } + `, + fragmentShader: ` + uniform float uTime; + uniform float uFlashIntensity; + varying vec2 vUv; + + void main() { + float t = uTime * 0.15; + + // Layered sine bands — each drifts at a different speed + float band1 = sin(vUv.x * 6.28318 + t * 1.3 + sin(vUv.x * 4.0 + t * 0.7) * 0.5); + float band2 = sin(vUv.x * 10.9956 - t * 0.9 + sin(vUv.x * 2.5 + t * 1.1) * 0.8); + float band3 = sin(vUv.x * 15.708 + t * 0.6 + sin(vUv.x * 6.0 - t * 0.4) * 0.3); + + // Map each band to a vertical curtain centred on a drifting Y + float b1 = smoothstep(0.0, 0.25, 1.0 - abs(vUv.y - (0.5 + band1 * 0.18))); + float b2 = smoothstep(0.0, 0.20, 1.0 - abs(vUv.y - (0.5 + band2 * 0.22))); + float b3 = smoothstep(0.0, 0.15, 1.0 - abs(vUv.y - (0.5 + band3 * 0.12))); + + float aurora = max(max(b1 * 0.7, b2 * 0.9), b3 * 0.5); + + // Colour: green → cyan → violet, drifting across the plane + float hueShift = sin(vUv.x * 2.0 + t * 0.3) * 0.5 + 0.5; + vec3 green = vec3(0.0, 1.0, 0.42); + vec3 cyan = vec3(0.0, 0.88, 1.0); + vec3 violet = vec3(0.52, 0.28, 1.0); + vec3 col = mix(mix(green, cyan, hueShift), violet, sin(t * 0.2 + vUv.x) * 0.5 + 0.5); + + // Flash: boost brightness and shift toward white + float intensity = 0.35 + uFlashIntensity * 0.65; + col = mix(col, vec3(1.0), uFlashIntensity * 0.45); + + // Soft edge fades + float vFade = smoothstep(0.0, 0.12, vUv.y) * smoothstep(1.0, 0.88, vUv.y); + float hFade = smoothstep(0.0, 0.07, vUv.x) * smoothstep(1.0, 0.93, vUv.x); + + float alpha = aurora * intensity * vFade * hFade; + gl_FragColor = vec4(col, alpha); + } + `, +}; + +const auroraGeometry = new THREE.PlaneGeometry(200, 60, 1, 1); +const auroraMaterial = new THREE.ShaderMaterial({ + uniforms: AuroraShader.uniforms, + vertexShader: AuroraShader.vertexShader, + fragmentShader: AuroraShader.fragmentShader, + transparent: true, + depthWrite: false, + blending: THREE.AdditiveBlending, + side: THREE.FrontSide, +}); + +const auroraMesh = new THREE.Mesh(auroraGeometry, auroraMaterial); +auroraMesh.position.set(0, 25, -80); +auroraMesh.rotation.x = -0.25; // tilt slightly toward viewer +scene.add(auroraMesh); + // === COMMIT HEATMAP === // Canvas-texture overlay on the floor. Each agent occupies a polar sector; // recent commits make that sector glow brighter. Activity decays over 24 h. @@ -1081,6 +1154,15 @@ function animate() { // Animate procedural clouds cloudMaterial.uniforms.uTime.value = elapsed; + // Animate northern lights — advance time and decay flash + auroraMaterial.uniforms.uTime.value = elapsed; + if (auroraMaterial.uniforms.uFlashIntensity.value > 0) { + auroraMaterial.uniforms.uFlashIntensity.value = Math.max( + 0, + auroraMaterial.uniforms.uFlashIntensity.value - 0.006 + ); + } + if (photoMode) { orbitControls.update(); } @@ -1516,6 +1598,34 @@ window.addEventListener('pr-notification', (/** @type {CustomEvent} */ event) => } }); +window.addEventListener('push-notification', (/** @type {CustomEvent} */ event) => { + console.log('[hermes] Push notification:', event.detail); + triggerAuroraPushFlash(); +}); + +// Poll Gitea for new commits (git pushes) — fallback when WebSocket is unavailable. +let _lastPushCommitSha = /** @type {string|null} */ (null); + +async function pollGiteaPushes() { + try { + const res = await fetch( + 'http://143.198.27.163:3000/api/v1/repos/Timmy_Foundation/the-nexus/commits?limit=1', + { headers: { 'Authorization': 'token dc0517a965226b7a0c5ffdd961b1ba26521ac592' } } + ); + if (!res.ok) return; + const commits = await res.json(); + if (!Array.isArray(commits) || !commits.length) return; + const latestSha = commits[0].sha; + if (_lastPushCommitSha !== null && latestSha !== _lastPushCommitSha) { + triggerAuroraPushFlash(); + } + _lastPushCommitSha = latestSha; + } catch (_) { /* network unavailable */ } +} + +pollGiteaPushes(); +setInterval(pollGiteaPushes, 30000); + // === SOVEREIGNTY EASTER EGG === const SOVEREIGNTY_WORD = 'sovereignty'; let sovereigntyBuffer = ''; @@ -1587,9 +1697,23 @@ function triggerSovereigntyEasterEgg() { } /** - * Triggers a visual flash effect for merge events: stars pulse bright, lines glow. + * Triggers a visual flash effect for push events: aurora flashes at medium intensity. + */ +function triggerAuroraPushFlash() { + auroraMaterial.uniforms.uFlashIntensity.value = Math.max( + auroraMaterial.uniforms.uFlashIntensity.value, + 0.5 + ); +} + +/** + * Triggers a visual flash effect for merge events: stars pulse bright, lines glow, + * and the northern lights surge to full intensity. */ function triggerMergeFlash() { + // Northern lights surge bright on merge + auroraMaterial.uniforms.uFlashIntensity.value = 1.0; + // Flash constellation lines bright blue-green const originalLineColor = constellationLines.material.color.getHex(); constellationLines.material.color.setHex(0x00ffff); diff --git a/ws-client.js b/ws-client.js index 603f6b2..fe61230 100644 --- a/ws-client.js +++ b/ws-client.js @@ -201,6 +201,11 @@ export class WebSocketClient { window.dispatchEvent(new CustomEvent('pr-notification', { detail: data })); break; + case 'push': + case 'push-notification': + window.dispatchEvent(new CustomEvent('push-notification', { detail: data })); + break; + case 'player-joined': window.dispatchEvent(new CustomEvent('player-joined', { detail: data })); break; -- 2.43.0