// SOLARGOD HUD controller — layer rows with per-layer status lines, LIVE/SCRUBBED // chip, breadcrumb, date readout. Ports GODSIGH's ui.js contract so layer modules // use the identical addLayer/setStatus/setLiveChip API. const rows = new Map(); // Register a layer row. Returns a handle so callers can flip the checkbox. export function addLayer(id, name, defaultOn, onToggle) { const row = document.createElement('div'); row.className = 'layer-row'; row.innerHTML = `
`; row.querySelector('.lname').textContent = name; const cb = row.querySelector('input'); cb.addEventListener('change', () => onToggle(cb.checked)); document.getElementById('layers').appendChild(row); rows.set(id, row); return { setChecked: (v) => { cb.checked = v; } }; } export function getLayerIds() { return [...rows.keys()]; } export function getLayerChecked(id) { const row = rows.get(id); return row ? row.querySelector('input').checked : false; } // Set checkbox AND fire onToggle (replays a real user toggle) — used to restore // hash state. No-op if unchanged. export function setLayerChecked(id, on) { const row = rows.get(id); if (!row) return; const cb = row.querySelector('input'); if (cb.checked !== on) { cb.checked = on; cb.dispatchEvent(new Event('change')); } } // state ∈ 'ok' | 'warn' | 'err' | 'off' export function setStatus(id, text, state = 'ok') { const row = rows.get(id); if (!row) return; row.querySelector('.lstatus').textContent = text; row.querySelector('.dot').dataset.state = state; } export function setLiveChip(isLive) { const chip = document.getElementById('live-chip'); if (!chip) return; chip.textContent = isLive ? 'LIVE' : 'SCRUBBED'; chip.title = isLive ? 'Clock is at wall-clock now' : 'Clock scrubbed off wall-clock now'; chip.dataset.state = isLive ? 'live' : 'scrub'; } // Breadcrumb: array of {id, name} from Sun down to the focused body. export function setBreadcrumb(trail, onClick) { const el = document.getElementById('breadcrumb'); if (!el) return; el.innerHTML = ''; trail.forEach((node, i) => { if (i > 0) { const sep = document.createElement('span'); sep.className = 'crumb-sep'; sep.textContent = '▸'; el.appendChild(sep); } const c = document.createElement('span'); c.className = 'crumb' + (i === trail.length - 1 ? ' current' : ''); c.textContent = node.name.toUpperCase(); if (i !== trail.length - 1 && onClick) { c.style.cursor = 'pointer'; c.addEventListener('click', () => onClick(node.id)); } el.appendChild(c); }); } export function setDateReadout(text, isLive) { const el = document.getElementById('date-readout'); if (el) { el.textContent = text; el.classList.toggle('scrubbed', !isLive); } } // Small transient toast (bottom-center) for one-shot notices. let toastTimer = null; export function toast(text, ms = 2200) { let el = document.getElementById('toast'); if (!el) return; el.textContent = text; el.classList.add('show'); clearTimeout(toastTimer); toastTimer = setTimeout(() => el.classList.remove('show'), ms); }