From e290de59873621ca8ba2db7269de398900729cd5 Mon Sep 17 00:00:00 2001 From: "Claude (Opus 4.6)" Date: Tue, 24 Mar 2026 04:15:22 +0000 Subject: [PATCH] [claude] Add floating commit banner sprites (#116) (#195) --- app.js | 119 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) diff --git a/app.js b/app.js index 6140bf0..b549577 100644 --- a/app.js +++ b/app.js @@ -361,6 +361,31 @@ function animate() { if (photoMode) { orbitControls.update(); } + + // Animate floating commit banners + const FADE_DUR = 1.5; + commitBanners.forEach(banner => { + const ud = banner.userData; + if (ud.spawnTime === null) { + if (elapsed < ud.startDelay) return; + ud.spawnTime = elapsed; + } + const age = elapsed - ud.spawnTime; + let opacity; + if (age < FADE_DUR) { + opacity = age / FADE_DUR; + } else if (age < ud.lifetime - FADE_DUR) { + opacity = 1; + } else if (age < ud.lifetime) { + opacity = (ud.lifetime - age) / FADE_DUR; + } else { + ud.spawnTime = elapsed + 3; + opacity = 0; + } + banner.material.opacity = opacity * 0.85; + banner.position.y = ud.baseY + Math.sin(elapsed * ud.floatSpeed + ud.floatPhase) * 0.4; + }); + composer.render(); } @@ -507,3 +532,97 @@ document.addEventListener('keydown', (e) => { window.addEventListener('beforeunload', () => { wsClient.disconnect(); }); + +// === COMMIT BANNERS === +const commitBanners = []; + +/** + * Creates a canvas texture for a commit banner. + * @param {string} hash - Short commit hash + * @param {string} message - Commit subject line + * @returns {THREE.CanvasTexture} + */ +function createCommitTexture(hash, message) { + const canvas = document.createElement('canvas'); + canvas.width = 512; + canvas.height = 64; + const ctx = canvas.getContext('2d'); + + ctx.fillStyle = 'rgba(0, 0, 16, 0.75)'; + ctx.fillRect(0, 0, 512, 64); + + ctx.strokeStyle = '#4488ff'; + ctx.lineWidth = 1; + ctx.strokeRect(0.5, 0.5, 511, 63); + + ctx.font = 'bold 11px "Courier New", monospace'; + ctx.fillStyle = '#4488ff'; + ctx.fillText(hash, 10, 20); + + ctx.font = '12px "Courier New", monospace'; + ctx.fillStyle = '#ccd6f6'; + const displayMsg = message.length > 54 ? message.slice(0, 54) + '\u2026' : message; + ctx.fillText(displayMsg, 10, 46); + + return new THREE.CanvasTexture(canvas); +} + +/** + * Fetches recent commits and spawns floating banner sprites. + */ +async function initCommitBanners() { + let commits; + try { + const res = await fetch( + 'http://143.198.27.163:3000/api/v1/repos/Timmy_Foundation/the-nexus/commits?limit=5', + { headers: { 'Authorization': 'token dc0517a965226b7a0c5ffdd961b1ba26521ac592' } } + ); + if (!res.ok) throw new Error('fetch failed'); + const data = await res.json(); + commits = data.map(/** @type {(c: any) => {hash: string, message: string}} */ c => ({ + hash: c.sha.slice(0, 7), + message: c.commit.message.split('\n')[0], + })); + } catch { + commits = [ + { hash: 'a1b2c3d', message: 'feat: depth of field effect on distant objects' }, + { hash: 'e4f5g6h', message: 'feat: photo mode with orbit controls' }, + { hash: 'i7j8k9l', message: 'feat: sovereignty easter egg animation' }, + { hash: 'm0n1o2p', message: 'feat: overview mode bird\'s-eye view' }, + { hash: 'q3r4s5t', message: 'feat: star field and constellation lines' }, + ]; + } + + const spreadX = [-7, -3.5, 0, 3.5, 7]; + const spreadY = [1.0, -1.5, 2.2, -0.8, 1.6]; + const spreadZ = [-1.5, -2.5, -1.0, -2.0, -1.8]; + + commits.forEach((commit, i) => { + const texture = createCommitTexture(commit.hash, commit.message); + const material = new THREE.SpriteMaterial({ + map: texture, + transparent: true, + opacity: 0, + depthWrite: false, + }); + const sprite = new THREE.Sprite(material); + sprite.scale.set(12, 1.5, 1); + sprite.position.set( + spreadX[i % spreadX.length], + spreadY[i % spreadY.length], + spreadZ[i % spreadZ.length] + ); + sprite.userData = { + baseY: spreadY[i % spreadY.length], + floatPhase: (i / commits.length) * Math.PI * 2, + floatSpeed: 0.25 + i * 0.07, + startDelay: i * 2.5, + lifetime: 12 + i * 1.5, + spawnTime: /** @type {number|null} */ (null), + }; + scene.add(sprite); + commitBanners.push(sprite); + }); +} + +initCommitBanners();