diff --git a/modules/bookshelves.js b/modules/bookshelves.js index 1680167..143b345 100644 --- a/modules/bookshelves.js +++ b/modules/bookshelves.js @@ -2,6 +2,7 @@ import * as THREE from 'three'; import { NEXUS } from './constants.js'; import { scene } from './scene-setup.js'; +import { fetchNexusCommits, fetchMergedPRs } from './data/gitea.js'; // === AGENT STATUS PANELS (declared early) === export const agentPanelSprites = []; @@ -37,29 +38,16 @@ function createCommitTexture(hash, message) { } export 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(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' }, - ]; - - initCommitBanners(); - } + const raw = await fetchNexusCommits(5); + const commits = raw.length > 0 + ? raw.map(c => ({ hash: c.sha.slice(0, 7), message: c.commit.message.split('\n')[0] })) + : [ + { 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]; @@ -208,23 +196,8 @@ function buildBookshelf(books, position, rotationY) { } export async function initBookshelves() { - let prs = []; - try { - const res = await fetch( - 'http://143.198.27.163:3000/api/v1/repos/Timmy_Foundation/the-nexus/pulls?state=closed&limit=20', - { headers: { 'Authorization': 'token dc0517a965226b7a0c5ffdd961b1ba26521ac592' } } - ); - if (!res.ok) throw new Error('fetch failed'); - const data = await res.json(); - prs = data - .filter(p => p.merged) - .map(p => ({ - prNum: p.number, - title: p.title - .replace(/^\[[\w\s]+\]\s*/i, '') - .replace(/\s*\(#\d+\)\s*$/, ''), - })); - } catch { + let prs = await fetchMergedPRs(20); + if (prs.length === 0) { prs = [ { prNum: 324, title: 'Model training status — LoRA adapters' }, { prNum: 323, title: 'The Oath — interactive SOUL.md reading' }, diff --git a/modules/data/gitea.js b/modules/data/gitea.js index fab63ef..1eeb4d3 100644 --- a/modules/data/gitea.js +++ b/modules/data/gitea.js @@ -139,4 +139,25 @@ export async function refreshAgentData() { } } +export async function fetchMergedPRs(limit = 20) { + try { + const res = await fetch( + `${GITEA_BASE}/repos/Timmy_Foundation/the-nexus/pulls?state=closed&limit=${limit}`, + { headers: { 'Authorization': `token ${GITEA_TOKEN}` } } + ); + if (!res.ok) return []; + const data = await res.json(); + return data + .filter(p => p.merged) + .map(p => ({ + prNum: p.number, + title: p.title + .replace(/^\[[\w\s]+\]\s*/i, '') + .replace(/\s*\(#\d+\)\s*$/, ''), + })); + } catch { + return []; + } +} + export { GITEA_BASE, GITEA_TOKEN, GITEA_REPOS, AGENT_NAMES, CACHE_MS as AGENT_STATUS_CACHE_MS }; diff --git a/modules/weather.js b/modules/weather.js index 7623686..9fcbfd4 100644 --- a/modules/weather.js +++ b/modules/weather.js @@ -4,6 +4,7 @@ import { scene, ambientLight } from './scene-setup.js'; import { cloudMaterial } from './platform.js'; import { rebuildRuneRing } from './effects.js'; import { S } from './state.js'; +import { fetchWeatherData } from './data/weather.js'; // === PORTAL HEALTH CHECKS === const PORTAL_HEALTH_CHECK_MS = 5 * 60 * 1000; @@ -162,20 +163,13 @@ function updateWeatherHUD(wx) { export async function fetchWeather() { try { - const url = `https://api.open-meteo.com/v1/forecast?latitude=${WEATHER_LAT}&longitude=${WEATHER_LON}¤t=temperature_2m,weather_code,wind_speed_10m,cloud_cover&temperature_unit=fahrenheit&wind_speed_unit=mph&forecast_days=1`; - const res = await fetch(url); - if (!res.ok) throw new Error('weather fetch failed'); - const data = await res.json(); - const cur = data.current; - const code = cur.weather_code; - const { condition, icon } = weatherCodeToLabel(code); - const cloudcover = typeof cur.cloud_cover === 'number' ? cur.cloud_cover : 50; - weatherState = { code, temp: cur.temperature_2m, wind: cur.wind_speed_10m, condition, icon, cloudcover }; - applyWeatherToScene(weatherState); - const cloudOpacity = 0.05 + (cloudcover / 100) * 0.55; - cloudMaterial.uniforms.uDensity.value = 0.3 + (cloudcover / 100) * 0.7; + const wx = await fetchWeatherData(); + weatherState = wx; + applyWeatherToScene(wx); + const cloudOpacity = 0.05 + (wx.cloudcover / 100) * 0.55; + cloudMaterial.uniforms.uDensity.value = 0.3 + (wx.cloudcover / 100) * 0.7; cloudMaterial.opacity = cloudOpacity; - updateWeatherHUD(weatherState); + updateWeatherHUD(wx); } catch { const descEl = document.getElementById('weather-desc'); if (descEl) descEl.textContent = 'Lempster NH';