From 6757ed7370bd16287ecc626850f37025be611f35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ya=C4=9F=C4=B1z=20O=C4=9Fuz?= Date: Wed, 19 Aug 2026 18:38:03 +0300 Subject: [PATCH] feat: add Quantum Beats customer-signal panel --- public/index.html | 122 +++++++++++++++++++++++++++++++++++++ scripts/check-frontend.mjs | 16 +++++ 2 files changed, 138 insertions(+) diff --git a/public/index.html b/public/index.html index 2e69d97..f1db5bc 100644 --- a/public/index.html +++ b/public/index.html @@ -290,6 +290,33 @@ .pr-action{color:var(--text-secondary);line-height:1.5} @media(max-width:760px){.pr-queue-kpis{grid-template-columns:1fr 1fr}.pr-queue-body{padding:0.75rem}} +/* Quantum beats */ +.beats-section{padding:2.5rem 0;border-top:1px solid var(--border)} +.beats-panel{border:1px solid var(--border);border-radius:var(--radius);background:var(--bg-elevated);overflow:hidden} +.beats-head{padding:1.25rem 1.5rem;border-bottom:1px solid var(--border);display:flex;align-items:flex-end;justify-content:space-between;gap:1rem;flex-wrap:wrap} +.beats-sub{font-size:0.82rem;color:var(--text-secondary);margin-top:0.35rem;max-width:720px;line-height:1.6} +.beats-body{padding:1.25rem;background:var(--bg)} +.beats-kpis{display:grid;grid-template-columns:repeat(4,1fr);gap:1px;background:var(--border);border-radius:var(--radius-sm);overflow:hidden;margin-bottom:1rem} +.beats-kpi{background:var(--bg-elevated);padding:0.9rem;min-width:0} +.beats-num{font-family:'JetBrains Mono',monospace;font-size:1.15rem;font-weight:800;letter-spacing:0} +.beats-label{font-size:0.62rem;font-weight:700;letter-spacing:0.07em;text-transform:uppercase;color:var(--text-muted);margin-top:0.2rem} +.beats-week{display:flex;align-items:center;justify-content:space-between;gap:0.75rem;flex-wrap:wrap;padding:0.7rem 0.9rem;border:1px solid var(--border);border-radius:var(--radius-xs);background:var(--bg-elevated);margin-bottom:1rem;font-size:0.78rem;color:var(--text-secondary)} +.beats-week strong{color:var(--accent)} +.beats-week.hidden{display:none} +.beats-table-wrap{border:1px solid var(--border);border-radius:var(--radius-sm);overflow:auto;-webkit-overflow-scrolling:touch} +.beats-table{min-width:560px;font-size:0.82rem} +.beats-table td{background:var(--bg)} +.beats-agent{font-weight:600;white-space:nowrap} +.beats-count{font-family:'JetBrains Mono',monospace;font-weight:700;white-space:nowrap} +.beats-share{display:flex;align-items:center;gap:0.6rem;min-width:10rem} +.beats-bar{flex:1;height:6px;background:var(--bg-hover);border-radius:100px;overflow:hidden} +.beats-bar-fill{height:100%;background:var(--accent);border-radius:100px} +.beats-pct{font-family:'JetBrains Mono',monospace;font-size:0.72rem;color:var(--text-muted);white-space:nowrap;width:2.6rem;text-align:right} +.beats-source{margin-top:0.9rem;font-size:0.72rem;color:var(--text-muted);line-height:1.6} +.beats-source a{color:var(--accent);text-decoration:none} +.beats-source a:hover{text-decoration:underline} +@media(max-width:760px){.beats-kpis{grid-template-columns:1fr 1fr}.beats-body{padding:0.75rem}} + /* API section */ .api-section{padding:2rem 0;border-top:1px solid var(--border)} .api-card{display:flex;align-items:center;justify-content:space-between;background:var(--bg-elevated);border:1px solid var(--border);border-radius:var(--radius-sm);padding:1rem 1.25rem;flex-wrap:wrap;gap:0.75rem} @@ -588,6 +615,40 @@

Source Freshness Audit

+ +
+
+
+
+
+

Quantum Beats

+

Customer world model view of the Daily Quantum Beat cadence on aibtc.news — cumulative beats by agent, rolling 7-day activity, and the underlying signals source. Cumulative totals never decrease on partial refreshes.

+
+ +
+
+
+ +
+ + + + + + + + + + + +
AgentBeatsShare of total
Loading quantum beats...
+
+
+
+
+
+
+
@@ -980,7 +1041,68 @@

Ecosystem

:'No open PRs in the contributor queue.'; } +function renderQuantumBeats(customer){ +const kpis=document.getElementById('beats-kpis'); +const body=document.getElementById('beats-body'); +const week=document.getElementById('beats-week'); +const asof=document.getElementById('beats-asof'); +const source=document.getElementById('beats-source'); +if(!kpis||!body||!week||!asof||!source)return; +const beats=customer&&customer.quantum_beats; +if(!beats||typeof beats!=='object'){ +asof.textContent='Customer model unavailable'; +kpis.innerHTML='
--
total beats
--
last 7 days
--
agents
--
active 7d
'; +week.classList.add('hidden'); +body.innerHTML='Quantum beats data is not available in /customer.json.'; +source.textContent=''; +return; +} +const byAgent=beats.by_agent&&typeof beats.by_agent==='object'?beats.by_agent:{}; +const entries=Object.entries(byAgent).map(([agent,count])=>({agent,count:Number(count)||0})).sort((a,b)=>b.count-a.count||a.agent.localeCompare(b.agent)); +const weekByAgent=beats.last_7d_by_agent&&typeof beats.last_7d_by_agent==='object'?beats.last_7d_by_agent:{}; +const weekEntries=Object.entries(weekByAgent).map(([agent,count])=>({agent,count:Number(count)||0})).sort((a,b)=>b.count-a.count||a.agent.localeCompare(b.agent)); +const total=Number(beats.total)||0; +const last7d=Number(beats.last_7d); +const activeWeek=weekEntries.filter(w=>w.count>0).length; +kpis.innerHTML=[ +{num:total,label:'total beats'}, +{num:Number.isFinite(last7d)?last7d:'--',label:'last 7 days'}, +{num:entries.length,label:'agents'}, +{num:activeWeek,label:'active 7d'} +].map(k=>`
${k.num}
${k.label}
`).join(''); +if(weekEntries.length&&Number.isFinite(last7d)){ +week.innerHTML=`Rolling 7-day cadence: ${last7d} beat${last7d===1?'':'s'}${weekEntries.length?` — ${weekEntries.map(w=>`${escapeHtml(w.agent)} (${w.count})`).join(', ')}`:''}Weekly activity is drawn from the latest signals refresh.`; +week.classList.remove('hidden'); +}else{ +week.classList.add('hidden'); +} +if(entries.length){ +const max=Math.max(...entries.map(x=>x.count),1); +body.innerHTML=entries.map(e=>{ +const barPct=Math.round(e.count/max*100); +const sharePct=total>0?Math.round(e.count/total*100):0; +return ` +${escapeHtml(e.agent)} +${e.count} +
${sharePct}% +`; +}).join(''); +}else{ +body.innerHTML='No quantum beats recorded yet.'; +} +const asofDate=customer&&customer.as_of?String(customer.as_of):''; +asof.textContent=asofDate?`Signals as of ${asofDate}`:'Current signals'; +const rawSrc=typeof beats.source==='string'?beats.source.trim():''; +let srcUrl='https://aibtc.news/api/signals'; +if(/^https:\/\//i.test(rawSrc)){ +try{srcUrl=new URL(rawSrc).toString();}catch{} +} +const srcNote=beats.total_source?`
${escapeHtml(String(beats.total_source))}`:''; +source.innerHTML=`Source: ${escapeHtml(srcUrl)}${srcNote}`; +} + function renderCustomerWorld(customer){ +renderQuantumBeats(customer); renderPayoutLedger(customer); renderPrWorkQueue(customer); } diff --git a/scripts/check-frontend.mjs b/scripts/check-frontend.mjs index 2f63054..2b4a7a8 100644 --- a/scripts/check-frontend.mjs +++ b/scripts/check-frontend.mjs @@ -19,6 +19,12 @@ for (const id of [ "freshness-body", "freshness-updates", "freshness-stale-list", + "beats-panel", + "beats-kpis", + "beats-week", + "beats-body", + "beats-asof", + "beats-source", "payout-panel", "payout-kpis", "payout-ledger-body", @@ -41,6 +47,16 @@ assert( "missing renderAffiliationReadiness()", ); assert(html.includes("function renderFreshnessAudit"), "missing renderFreshnessAudit()"); +assert(html.includes("function renderQuantumBeats"), "missing renderQuantumBeats()"); +assert(html.includes("renderQuantumBeats(customer);"), "renderCustomerWorld must wire in renderQuantumBeats"); +assert(html.includes("quantum_beats"), "quantum beats panel must surface the quantum_beats customer world model"); +assert(html.includes("last_7d"), "quantum beats panel must surface rolling 7-day activity"); +assert(html.includes("by_agent"), "quantum beats panel must surface the per-agent breakdown"); +assert( + html.includes("Quantum beats data is not available in /customer.json."), + "quantum beats panel must degrade gracefully when the customer model is missing", +); +assert(html.includes("Rolling 7-day cadence"), "quantum beats panel must surface the rolling 7-day cadence"); assert(html.includes("function renderPayoutLedger"), "missing renderPayoutLedger()"); assert(html.includes("function renderPrWorkQueue"), "missing renderPrWorkQueue()"); assert(html.includes("function renderCompareView"), "missing renderCompareView()");