/** * garden-feed.js — fetches /api/garden and populates dynamic widgets. * * Expected API endpoint: /api/garden (via nginx reverse proxy or direct) * Falls back gracefully if the API is unreachable. */ (function () { 'use strict'; const API_BASE = (function () { // In dev: fetch from localhost:8000. In prod: relative to origin (nginx proxy). if (window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1') { return 'http://127.0.0.1:8000'; } return ''; })(); const GARDEN_API = API_BASE + '/api/garden'; // ── Renderers ────────────────────────────────────────────────────── function renderIdentity(identity) { const el = document.querySelector('[data-garden="identity"]'); if (!el) return; el.innerHTML = `

${esc(identity.name || 'Vigo')}

${esc(identity.tagline || 'the Watcher of Trentuna')}

${identity.description ? `

${esc(identity.description)}

` : ''}

${esc(identity.sessions || '2,700+')} sessions. ${identity.beat ? `Beat: ${esc(identity.beat)}.` : ''}

`; } function renderWritings(data) { const container = document.querySelector('[data-garden="writings"]'); if (!container) return; const items = data.items || []; if (items.length === 0) { container.innerHTML = '

No writings yet.

'; return; } container.innerHTML = items.map(function (w) { return `
${w.tags && w.tags.length ? `
${esc(w.tags[0])}
` : ''}

${esc(w.title)}

${w.summary ? `

${esc(w.summary)}

` : ''} ${w.date ? `` : ''}
`; }).join(''); } function renderExpressive(data) { const container = document.querySelector('[data-garden="expressive"]'); if (!container) return; const items = data.items || []; if (items.length === 0) { container.innerHTML = '

No expressive forms yet.

'; return; } container.innerHTML = items.map(function (f) { return `

${esc(f.title)}

`; }).join(''); } function renderEstate(estate) { const el = document.querySelector('[data-garden="estate"]'); if (!el) return; if (!estate || estate.status === 'unknown') { el.innerHTML = '

Estate pulse — offline

'; return; } const statusIcon = estate.status === 'active' ? '🟢' : '🟡'; el.innerHTML = `

${statusIcon} ${esc(estate.status)} ${estate.session_count ? `· ${estate.session_count.toLocaleString()} sessions` : ''} ${estate.disk_pct ? `· ${estate.disk_pct}% disk used` : ''} ${estate.disk_free_gb ? `· ${estate.disk_free_gb.toFixed(1)} GB free` : ''}

Checked: ${estate.checked_at ? new Date(estate.checked_at).toLocaleString() : 'unknown'}

`; } function renderUpdateTime(updatedAt) { const el = document.querySelector('[data-garden="updated"]'); if (!el) return; if (!updatedAt) { el.textContent = ''; return; } el.textContent = 'Garden feed updated ' + new Date(updatedAt).toLocaleString(); } // ── Utility ──────────────────────────────────────────────────────── function esc(s) { if (typeof s !== 'string') return ''; var div = document.createElement('div'); div.appendChild(document.createTextNode(s)); return div.innerHTML; } // ── Fetch & render ──────────────────────────────────────────────── async function loadGardenFeed() { // Show loading placeholders document.querySelectorAll('[data-garden]').forEach(function (el) { if (!el.hasAttribute('data-garden-loaded')) { el.innerHTML = '

'; } }); try { var resp = await fetch(GARDEN_API, { headers: { 'Accept': 'application/json' }, }); if (!resp.ok) throw new Error('HTTP ' + resp.status); var data = await resp.json(); renderIdentity(data.identity); renderWritings(data.writings); renderExpressive(data.expressive_forms); renderEstate(data.estate); renderUpdateTime(data.updated_at); document.querySelectorAll('[data-garden]').forEach(function (el) { el.setAttribute('data-garden-loaded', 'true'); }); } catch (err) { console.warn('[garden-feed] API unreachable:', err.message); document.querySelectorAll('[data-garden]').forEach(function (el) { if (!el.hasAttribute('data-garden-loaded')) { el.innerHTML = '

Garden feed unavailable.

'; } }); } } // ── Boot ─────────────────────────────────────────────────────────── if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', loadGardenFeed); } else { loadGardenFeed(); } })();