Spaces:
Sleeping
Sleeping
Nhughes09
Complete UI redesign: add Introduction, Formulas grid, and harden JSON renderers for weekly tests
2681f50 | // DOME COSMOLOGY - VANILLA JS FRONTEND | |
| document.addEventListener('DOMContentLoaded', async () => { | |
| // Inject master data into the hidden LD+JSON script tag for AI scrapers | |
| const aiScriptNode = document.getElementById('dome-predictions-data'); | |
| if (aiScriptNode) { | |
| const fullPayload = { | |
| metadata: { version: "V49.2", updated: new Date().toISOString() }, | |
| master_predictions: typeof PREDICTIONS !== 'undefined' ? PREDICTIONS : [], | |
| weekly_active_tests: typeof WEEKLY_DATA !== 'undefined' ? WEEKLY_DATA : {} | |
| }; | |
| aiScriptNode.textContent = JSON.stringify(fullPayload, null, 2); | |
| } | |
| // Set the weekly label | |
| const weekLabel = document.getElementById('week-label'); | |
| if(weekLabel && typeof WEEKLY_DATA !== 'undefined') { | |
| weekLabel.textContent = `${WEEKLY_DATA.week_start} to ${WEEKLY_DATA.week_end}`; | |
| } | |
| try { | |
| const resArgs = await fetch('api/current/results.json?v=' + Date.now()); | |
| if (resArgs.ok) { | |
| window.RAW_RESULTS = await resArgs.json(); | |
| } else { | |
| console.warn("Could not load api/current/results.json, falling back to empty."); | |
| window.RAW_RESULTS = []; | |
| } | |
| } catch (e) { | |
| console.error("Fetch failed", e); | |
| window.RAW_RESULTS = []; | |
| } | |
| // Attempt to compute the scored results manually | |
| window.SCORED_RESULTS = []; | |
| const allPreds = (typeof PREDICTIONS !== 'undefined' ? PREDICTIONS : []).concat( | |
| typeof WEEKLY_DATA !== 'undefined' ? WEEKLY_DATA.predictions || [] : [] | |
| ); | |
| const predMap = {}; | |
| allPreds.forEach(p => predMap[p.id] = p); | |
| window.RAW_RESULTS.forEach(r => { | |
| let p = predMap[r.id]; | |
| let sr = null; | |
| if (p && typeof scoreResult === 'function') { | |
| sr = scoreResult(p, r.observed); | |
| } | |
| let vr = sr ? sr.verdict.toLowerCase() : r.auto_verdict; | |
| // Merge them | |
| window.SCORED_RESULTS.push({ | |
| ...r, | |
| auto_verdict: vr || r.auto_verdict, | |
| computed_score: sr | |
| }); | |
| }); | |
| initScorecard(window.SCORED_RESULTS); | |
| renderGrids(window.SCORED_RESULTS); | |
| initFormulas(); | |
| initNav(); | |
| }); | |
| async function initFormulas() { | |
| try { | |
| const resArgs = await fetch('api/current/formulas.json?v=' + Date.now()); | |
| if (resArgs.ok) { | |
| const rawForms = await resArgs.json(); | |
| const coreFormulas = rawForms.slice(0, 7); | |
| const grid = document.getElementById('formulas-grid'); | |
| if (!grid) return; | |
| let html = ''; | |
| coreFormulas.forEach(f => { | |
| html += ` | |
| <div class="glass-card" style="display:flex; flex-direction:column;"> | |
| <div class="card-topbar"> | |
| <span class="id-tag">${f.id}</span> | |
| <div class="status-indicator status-confirmed" style="color:#34d399;"> | |
| <div class="status-dot" style="background:#34d399"></div> | |
| ${f.category ? f.category.toUpperCase().replace('_', ' ') : 'CORE'} | |
| </div> | |
| </div> | |
| <h3 class="card-title" style="margin-bottom:0.75rem; color:var(--text-heading);">${f.name}</h3> | |
| <div style="font-family:monospace; background:rgba(0,0,0,0.5); padding:1rem; border-radius:4px; border:1px dashed var(--border-subtle); color:var(--accent-purple); font-size:0.95rem; margin-bottom:1rem; overflow-x:auto;"> | |
| ${f.formula} | |
| </div> | |
| ${f.derivation ? `<p style="font-size:0.85rem; color:var(--text-secondary); margin-bottom:1rem; flex-grow:1; line-height:1.5;">${f.derivation}</p>` : ''} | |
| <div style="font-size:0.8rem; letter-spacing:0.05em; color:var(--accent-sky); margin-top:auto;"> | |
| SOURCE SCRIPT: ${f.source_file} | |
| </div> | |
| </div> | |
| `; | |
| }); | |
| grid.innerHTML = html; | |
| } | |
| } catch (e) { | |
| console.error("Failed to load formulas", e); | |
| } | |
| } | |
| function computeScorecard(results) { | |
| return { | |
| wins: results.filter(r => { | |
| const v = (r.auto_verdict || "").toLowerCase(); | |
| return ['confirmed', 'confirmed_marginal', 'confirmed_strong', 'overshoot_investigate'].includes(v); | |
| }).length, | |
| below_threshold: results.filter(r => | |
| (r.auto_verdict || "").toLowerCase() === 'below_detection_threshold' | |
| ).length, | |
| investigating: results.filter(r => | |
| (r.auto_verdict || "").toLowerCase() === 'overshoot_investigate' | |
| ).length, | |
| falsified: results.filter(r => | |
| ((r.auto_verdict || "").toLowerCase() === 'falsified' && r.counts_against_model === true) | |
| ).length, | |
| pending: results.filter(r => | |
| (r.auto_verdict || "").toLowerCase() === 'pending' | |
| ).length | |
| } | |
| } | |
| function initScorecard(results) { | |
| if (!results || results.length === 0) { | |
| document.getElementById('scorecard').innerHTML = '<div class="score-hud">Loading...</div>'; | |
| return; | |
| } | |
| const scores = computeScorecard(results); | |
| const pendingTotal = typeof PREDICTIONS !== 'undefined' ? PREDICTIONS.filter(p => !p.scoring_matrix || p.status === 'pending').length : 0; | |
| const activeTestCount = typeof WEEKLY_DATA !== 'undefined' && WEEKLY_DATA.predictions ? WEEKLY_DATA.predictions.length : 0; | |
| // The user rules strictly states the wins, investigating, falsified must come exactly from the results array computations. | |
| const html = ` | |
| <div class="score-hud"><span>${scores.wins}</span> WINS</div> | |
| <div class="score-hud" style="color:var(--accent-blue);"><span>${activeTestCount}</span> LIVE TESTS</div> | |
| <div class="score-hud"><span>${pendingTotal}</span> PENDING</div> | |
| <div class="score-hud" style="color:var(--status-falsified)"><span>${scores.falsified}</span> FALSIFIED</div> | |
| `; | |
| document.getElementById('scorecard').innerHTML = html; | |
| } | |
| function buildClaimsTable(computed_score) { | |
| if (!computed_score || !computed_score.claim_breakdown || computed_score.claim_breakdown.length === 0) return ''; | |
| let rows = computed_score.claim_breakdown.map(c => ` | |
| <div style="display:flex; justify-content:space-between; padding:0.25rem 0; border-bottom: 1px solid rgba(255,255,255,0.05);"> | |
| <span style="font-size:0.85rem; color:var(--text-secondary);">${c.claim}</span> | |
| <span style="font-size:0.85rem; font-weight:bold; ${c.result === 'CORRECT' ? 'color:#34d399;' : 'color:#ef4444;'}"> | |
| ${c.result === 'CORRECT' ? '✓' : '✗'} [${c.result === 'CORRECT' ? '+'+c.points : c.points}] | |
| </span> | |
| </div> | |
| `).join(''); | |
| return ` | |
| <div class="glass-card" style="background: rgba(0,0,0,0.3); padding: 1rem; border: 1px dashed var(--border-subtle); margin-bottom: 1rem;"> | |
| <div style="font-size: 0.75rem; letter-spacing: 0.05em; color: var(--text-muted); margin-bottom: 0.5rem; text-transform: uppercase; display:flex; justify-content:space-between;"> | |
| <span>MECHANISM CONFIRMATION MATRIX</span> | |
| <span style="color:var(--text-primary)">Score: ${computed_score.total_score}/${computed_score.max_score} (${Math.round(computed_score.percentage*100)}%)</span> | |
| </div> | |
| ${rows} | |
| <div style="margin-top:0.75rem; font-size:0.85rem; color:var(--accent-sky);"> | |
| <strong>Model-Distinguishing Claims:</strong> ${computed_score.model_distinguishing_passed} / ${computed_score.model_distinguishing_total} Passed | |
| </div> | |
| </div> | |
| `; | |
| } | |
| function createCardHTML(p, resultMap, isWeekly = false) { | |
| let resultLine = ''; | |
| let claimsHTML = ''; | |
| const resNode = resultMap[p.id]; | |
| let displayLabel = "PENDING"; | |
| let statusClass = "pending"; | |
| let colorStyle = "color:var(--text-muted);"; | |
| if (resNode && resNode.auto_verdict) { | |
| displayLabel = resNode.display_label || resNode.auto_verdict.replace(/_/g, ' ').toUpperCase(); | |
| statusClass = resNode.auto_verdict.replace(/_/g, '-'); | |
| const vd = resNode.auto_verdict.toLowerCase(); | |
| if(['confirmed', 'confirmed_marginal', 'confirmed_strong'].includes(vd)) { | |
| colorStyle = 'color:#34d399;'; | |
| } else if (vd === 'below_detection_threshold') { | |
| colorStyle = 'color:#eab308;'; | |
| } else if (vd === 'falsified') { | |
| colorStyle = 'color:#ef4444;'; | |
| } else if (vd === 'overshoot_investigate') { | |
| colorStyle = 'color:#0ea5e9;'; | |
| } else if (vd === 'inconclusive') { | |
| colorStyle = 'color:#9ca3af;'; | |
| } | |
| const missingNote = vd === 'below_detection_threshold' ? | |
| `<div style="font-size:0.85rem; color:#eab308; margin-top:0.25rem;">Signal below detection limit - prediction untestable at this sensitivity. Not a model failure.</div>` : ''; | |
| const overshootNote = (resNode.overshoot_ratio && resNode.overshoot_ratio > 1.0 && (resNode.direction_correct || resNode.computed_score)) ? | |
| `<div style="font-size:0.85rem; color:#34d399; margin-top:0.25rem;">Signal ${resNode.overshoot_ratio}x stronger than predicted - mechanism confirmed, magnitude being refined.</div>` : ''; | |
| const sigmaText = resNode.sigma_distance !== undefined && resNode.sigma_distance > 0 ? | |
| `<span style="margin-right: 0.75rem;">${resNode.sigma_distance}σ from prediction</span>` : ''; | |
| const snrVal = (resNode.observed && resNode.observed.snr) ? resNode.observed.snr : null; | |
| const snrSuff = (snrVal >= 2.0) || resNode.snr_sufficient; | |
| const snrText = snrVal ? | |
| `<span style="margin-right: 0.75rem;">SNR: ${snrVal} (${snrSuff ? 'detectable' : 'sub-threshold'})</span>` : ''; | |
| const dirCorrect = resNode.direction_correct; | |
| const dirText = dirCorrect !== undefined ? | |
| `<span>Direction: ${dirCorrect ? '✓ Correct' : '✗ Wrong'}</span>` : ''; | |
| const obsVal = resNode.observed && resNode.observed.value !== undefined ? resNode.observed.value : (p.result_value || resNode.observed?.peak_nT || ''); | |
| resultLine = ` | |
| <div class="data-matrix" style="border-top:none; padding-top:0; margin-top:-0.5rem;"> | |
| <div class="data-row"> | |
| <span class="data-label" style="${colorStyle}">OBSERVED RESULT</span> | |
| <span class="data-value" style="${colorStyle}">${obsVal}</span> | |
| </div> | |
| ${(sigmaText || snrText || dirText) ? `<div style="font-size: 0.8rem; color: var(--text-secondary); margin-top:0.25rem;">${sigmaText}${snrText}${dirText}</div>` : ''} | |
| ${missingNote} | |
| ${overshootNote} | |
| </div>`; | |
| claimsHTML = buildClaimsTable(resNode.computed_score); | |
| } else if (p.status !== 'pending' && p.result_value) { | |
| // Fallback for missing JSON results | |
| const isSuccessful = p.status === 'confirmed'; | |
| colorStyle = isSuccessful ? 'color:#34d399;' : 'color:#ef4444;'; | |
| displayLabel = p.status.toUpperCase(); | |
| statusClass = p.status; | |
| resultLine = ` | |
| <div class="data-matrix" style="border-top:none; padding-top:0; margin-top:-0.5rem;"> | |
| <div class="data-row"> | |
| <span class="data-label" style="${colorStyle}">OBSERVED RESULT</span> | |
| <span class="data-value" style="${colorStyle}">${p.result_value !== null ? p.result_value : ''}</span> | |
| </div> | |
| </div>`; | |
| } | |
| const testDateDisplay = isWeekly ? 'Live This Week' : (p.test_date ? p.test_date.split('T')[0] : 'Ongoing'); | |
| const predVal = (p.point_prediction && p.point_prediction.value !== null) ? p.point_prediction.value : (p.prediction ? p.prediction.value : (p.predicted_value !== undefined ? p.predicted_value : (p.prediction_nT || ''))); | |
| const unitVal = p.point_prediction ? (p.point_prediction.unit || 'nT') : (p.prediction ? (p.prediction.unit || 'nT') : 'nT'); | |
| let recalibratedHTML = ''; | |
| if (p.point_prediction_recalibrated) { | |
| recalibratedHTML = `<div style="font-size:0.85rem; color:var(--accent-blue); margin-top:2px; font-weight:normal; letter-spacing:0.02em;">[W004 Empirical Baseline] ${p.point_prediction_recalibrated.value} <span style="font-size:0.7rem; color:var(--text-muted);">${p.point_prediction_recalibrated.unit || 'nT'}</span></div>`; | |
| } | |
| // Hardening string parsers for mixed JSON payload types | |
| const mechStr = typeof p.mechanism === 'string' ? p.mechanism : (p.mechanism && p.mechanism.description ? p.mechanism.description : 'Empirical Validation'); | |
| const verifySource = p.verification_source || p.data_source || 'INTERMAGNET'; | |
| const formStr = p.formula || (p.derivation ? p.derivation.formula : null); | |
| return ` | |
| <div class="glass-card"> | |
| <div class="card-topbar"> | |
| <span class="id-tag">${p.id}</span> | |
| <div class="status-indicator status-${statusClass}" style="${colorStyle}"> | |
| <div class="status-dot" style="background:${colorStyle.split(':')[1].replace(';','')}"></div> | |
| ${displayLabel} | |
| </div> | |
| </div> | |
| <h3 class="card-title">${p.title || p.station}</h3> | |
| <p class="card-desc">${p.description || (p.mechanism ? p.mechanism.description : '')}</p> | |
| <div class="data-matrix"> | |
| <div class="data-row"> | |
| <span class="data-label">TARGET WINDOW</span> | |
| <span class="data-value accent">${testDateDisplay}</span> | |
| </div> | |
| <div class="data-row" style="align-items:flex-start;"> | |
| <span class="data-label" style="margin-top:0.25rem;">MODEL PREDICTION</span> | |
| <div style="display:flex; flex-direction:column; align-items:flex-end; text-align:right;"> | |
| <span class="data-value" style="margin-bottom:0px;">${predVal !== null && predVal !== undefined ? predVal : ''} <span style="font-size:0.8rem; color:var(--text-muted)">${unitVal}</span></span> | |
| ${recalibratedHTML} | |
| </div> | |
| </div> | |
| </div> | |
| ${resultLine} | |
| ${claimsHTML} | |
| <div class="card-mechanism" style="margin-bottom: 0.5rem;"> | |
| <div style="margin-bottom:0.25rem;"><strong style="color:var(--text-primary)">Mechanism:</strong> ${mechStr}</div> | |
| <div><strong style="color:var(--text-primary)">Verification Anchor:</strong> ${verifySource}</div> | |
| </div> | |
| ${formStr ? ` | |
| <div class="glass-card" style="background: rgba(0,0,0,0.1); padding: 1rem; border: 1px dashed var(--border-subtle); margin-bottom: 1rem;"> | |
| <div style="font-size: 0.75rem; letter-spacing: 0.05em; color: var(--text-muted); margin-bottom: 0.5rem; text-transform: uppercase;">DERIVATION MECHANISM</div> | |
| <div style="font-size: 0.9rem; margin-bottom: 0.25rem; font-family:monospace; color:var(--accent-purple)"><strong style="color:var(--text-primary)">Formula:</strong> ${formStr}</div> | |
| </div> | |
| ` : ''} | |
| ${isWeekly ? `<a href="proofs/weekly_predictions_${typeof WEEKLY_DATA !== 'undefined' ? WEEKLY_DATA.week_start : ''}.json.ots" download class="btn-verify btn-primary" style="margin-top:auto;" data-proof-type="opentimestamps" data-sha256="${p.sha256}" title="Download Bitcoin blockchain anchor proof"> | |
| <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg> | |
| GET CRYPTOGRAPHIC PROOF (.OTS) | |
| </a>` : ''} | |
| <div class="sha-fingerprint">SHA-256: ${p.sha256 || p.manifest_sha256}</div> | |
| </div> | |
| `; | |
| } | |
| function renderGrids(results) { | |
| const confirmedGrid = document.getElementById('confirmed-grid'); | |
| const pendingGrid = document.getElementById('pending-grid'); | |
| const weeklyGrid = document.getElementById('weekly-grid'); | |
| const resultMap = {}; | |
| if(results) { | |
| results.forEach(r => resultMap[r.id] = r); | |
| } | |
| // 1. Render the Long-Term arrays | |
| let confHTML = '', pendingHTML = ''; | |
| if (typeof PREDICTIONS !== 'undefined') { | |
| PREDICTIONS.forEach(p => { | |
| const resNode = resultMap[p.id]; | |
| const isConfirmed = resNode ? ['confirmed', 'confirmed_marginal', 'confirmed_strong', 'overshoot_investigate'].includes(resNode.auto_verdict || '') : p.status === 'confirmed'; | |
| const isFalsified = resNode ? (resNode.auto_verdict === 'falsified') : p.status === 'falsified'; | |
| const card = createCardHTML(p, resultMap, false); | |
| if (isConfirmed || isFalsified || (resNode && resNode.auto_verdict && resNode.auto_verdict !== 'pending')) { | |
| confHTML += card; | |
| } else { | |
| pendingHTML += card; | |
| } | |
| }); | |
| } | |
| if (confirmedGrid) confirmedGrid.innerHTML = confHTML; | |
| if (pendingGrid) pendingGrid.innerHTML = pendingHTML; | |
| // 2. Render the new Weekly active tests | |
| let weekActiveHTML = ''; | |
| let weekPastHTML = ''; | |
| if (typeof WEEKLY_DATA !== 'undefined' && WEEKLY_DATA.predictions) { | |
| let now = Date.now(); | |
| WEEKLY_DATA.predictions.forEach(p => { | |
| const resNode = resultMap[p.id]; | |
| // The user explicitly requested to partition based ONLY on status | |
| let isPast = (p.status && p.status !== 'pending'); | |
| if (resNode && resNode.auto_verdict && resNode.auto_verdict !== 'pending') { | |
| isPast = true; | |
| } | |
| const card = createCardHTML(p, resultMap, true); | |
| if (isPast) { | |
| weekPastHTML += card; | |
| } else { | |
| weekActiveHTML += card; | |
| } | |
| }); | |
| } | |
| const wActiveGrid = document.getElementById('weekly-active-grid'); | |
| const wPastGrid = document.getElementById('weekly-past-grid'); | |
| if (wActiveGrid) wActiveGrid.innerHTML = weekActiveHTML || '<p style="color:var(--text-muted); grid-column:1/-1;">No pending test runs this week.</p>'; | |
| if (wPastGrid) wPastGrid.innerHTML = weekPastHTML || '<p style="color:var(--text-muted); grid-column:1/-1;">No evaluated runs this week yet.</p>'; | |
| } | |
| function initNav() { | |
| const btns = document.querySelectorAll('.nav-pill'); | |
| const sections = document.querySelectorAll('.view-section'); | |
| btns.forEach(btn => { | |
| btn.addEventListener('click', () => { | |
| btns.forEach(b => b.classList.remove('active')); | |
| sections.forEach(s => s.classList.remove('active')); | |
| btn.classList.add('active'); | |
| const target = btn.getAttribute('data-target'); | |
| if (document.getElementById(target)) { | |
| document.getElementById(target).classList.add('active'); | |
| } | |
| }); | |
| }); | |
| } | |