refactor: modularize app.js into ES module architecture
Split the monolithic 5393-line app.js into 32 focused ES modules under modules/ with a thin ~330-line orchestrator. No bundler required — runs in-browser via import maps. Module structure: core/ — scene, ticker, state, theme, audio data/ — gitea, weather, bitcoin, loaders terrain/ — stars, clouds, island effects/ — matrix-rain, energy-beam, lightning, shockwave, rune-ring, gravity-zones panels/ — heatmap, sigil, sovereignty, dual-brain, batcave, earth, agent-board, lora-panel portals/ — portal-system, commit-banners narrative/ — bookshelves, oath, chat utils/ — perlin All files pass node --check. No new dependencies. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
62
modules/portals/commit-banners.js
Normal file
62
modules/portals/commit-banners.js
Normal file
@@ -0,0 +1,62 @@
|
||||
// modules/portals/commit-banners.js — Floating commit banner sprites
|
||||
import * as THREE from 'three';
|
||||
import { fetchRecentCommitsForBanners } from '../data/gitea.js';
|
||||
|
||||
const commitBanners = [];
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
export async function init(scene) {
|
||||
const commits = await fetchRecentCommitsForBanners();
|
||||
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: null,
|
||||
zoomLabel: `Commit: ${commit.hash}`,
|
||||
};
|
||||
scene.add(sprite);
|
||||
commitBanners.push(sprite);
|
||||
});
|
||||
}
|
||||
|
||||
export function update(elapsed) {
|
||||
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;
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user