[object Object]

← back to Model Arena

arena: cost dashboard across top (today/week/month/year/all-time)

595106f7488b0798ebcc6f8f294b688b6a7aeee8 · 2026-07-23 12:17:24 -0700 · Steve

Files touched

Diff

commit 595106f7488b0798ebcc6f8f294b688b6a7aeee8
Author: Steve <steve@designerwallcoverings.com>
Date:   Thu Jul 23 12:17:24 2026 -0700

    arena: cost dashboard across top (today/week/month/year/all-time)
---
 public/index.html | 21 +++++++++++++++++++++
 server.js         | 23 +++++++++++++++++++++++
 2 files changed, 44 insertions(+)

diff --git a/public/index.html b/public/index.html
index 86d7e64..dac9e3d 100644
--- a/public/index.html
+++ b/public/index.html
@@ -143,6 +143,10 @@ tr.clk{cursor:pointer}tr.clk:hover td{background:rgba(0,229,255,.06)}
 .champ{border:1px solid var(--gold);background:rgba(255,199,46,.08);padding:14px 18px;text-align:center;box-shadow:0 0 22px rgba(255,199,46,.25)}
 .champ .k{font-size:26px}.champ b{color:var(--gold);font-size:16px}
 .pane .buildbtm{display:block;width:100%;margin-top:8px;padding:10px;font-weight:700;letter-spacing:1px;border-radius:4px;cursor:pointer}
+.costbar{position:sticky;top:0;z-index:4;display:flex;gap:20px;align-items:center;flex-wrap:wrap;background:rgba(9,10,18,.96);backdrop-filter:blur(6px);border-bottom:1px solid var(--line);padding:9px 22px;font-size:12px}
+.costbar .cb-lbl{color:var(--gold);letter-spacing:1px;text-transform:uppercase;font-size:11px}
+.costbar .cb-item{color:var(--dim)}.costbar .cb-item b{color:var(--txt);margin-left:5px;font-weight:700}
+.costbar .cb-item b.hot{color:var(--gold)}
 </style>
 </head>
 <body>
@@ -155,6 +159,14 @@ tr.clk{cursor:pointer}tr.clk:hover td{background:rgba(0,229,255,.06)}
     <button id="nav-board">Leaderboard</button>
   </nav>
 </header>
+<div class="costbar" id="costbar" title="Total metered API spend across the arena (local models + Claude Max CLI are $0)">
+  <span class="cb-lbl">💸 Arena spend</span>
+  <span class="cb-item">Today <b id="cb-today">$0</b></span>
+  <span class="cb-item">Week <b id="cb-week">$0</b></span>
+  <span class="cb-item">Month <b id="cb-month">$0</b></span>
+  <span class="cb-item">Year <b id="cb-year">$0</b></span>
+  <span class="cb-item">All-time <b id="cb-all">$0</b></span>
+</div>
 <main>
   <section id="challenges" class="view on">
     <div class="controls">
@@ -621,6 +633,15 @@ function renderH2H(d){
 }
 
 loadModels().then(loadList);
+
+async function loadCosts(){
+  try{ const c=await (await fetch(API+'/api/costs')).json();
+    const f=n=>'$'+(Number(n)||0).toFixed(2);
+    const set=(id,v)=>{const e=document.getElementById(id); if(e){e.textContent=f(v); e.classList.toggle('hot',(Number(v)||0)>0);}};
+    set('cb-today',c.today); set('cb-week',c.week); set('cb-month',c.month); set('cb-year',c.year); set('cb-all',c.all);
+  }catch(e){}
+}
+loadCosts(); setInterval(loadCosts, 30000);
 </script>
 </body>
 </html>
diff --git a/server.js b/server.js
index 9d8881a..cc09fe0 100644
--- a/server.js
+++ b/server.js
@@ -779,6 +779,29 @@ end tell`;
     return send(res, 200, { model: { id: mid, label: mo.label, kind: mo.kind }, stats: full, gallery });
   }
 
+  if (p === '/api/costs') {
+    const now = new Date();
+    const sDay = new Date(now.getFullYear(), now.getMonth(), now.getDate()).getTime();
+    const dow = (now.getDay() + 6) % 7;
+    const sWeek = new Date(now.getFullYear(), now.getMonth(), now.getDate() - dow).getTime();
+    const sMonth = new Date(now.getFullYear(), now.getMonth(), 1).getTime();
+    const sYear = new Date(now.getFullYear(), 0, 1).getTime();
+    let today = 0, week = 0, month = 0, year = 0, all = 0;
+    try {
+      for (const line of fs.readFileSync(COST_FILE, 'utf8').split('\n')) {
+        if (!line.trim()) continue;
+        let d; try { d = JSON.parse(line); } catch { continue; }
+        const cst = d.cost_usd || 0; if (!cst) continue;
+        all += cst;
+        const t = Date.parse(d.ts || ''); if (isNaN(t)) continue;
+        if (t >= sYear) year += cst;
+        if (t >= sMonth) month += cst;
+        if (t >= sWeek) week += cst;
+        if (t >= sDay) today += cst;
+      }
+    } catch {}
+    return send(res, 200, { today, week, month, year, all });
+  }
   if (p === '/api/ledger') {
     const cat = u.searchParams.get('category') || '';
     const l = buildLedger(cat || null);

← d6dfe90 auto-save: 2026-07-23T11:50:46 (15 files) — data/challenges.  ·  back to Model Arena  ·  auto-save: 2026-07-23T12:20:57 (15 files) — data/challenges. 34b99c1 →