← back to Ken
Ken: live-run status chip in dashboard header (motion graphics)
a327393ff82239882cc28079fab23e4d250840d5 · 2026-07-14 10:11:54 -0700 · Steve Abrams
- GET /api/live-run — day X/N, $ spent today vs daily cap, auto-off date, days left
- Layout.jsx status bar: animated LiveRunChip (pulsing glow, shimmer sweep, blinking
live dot, flowing progress bar, rAF count-up on $ spent). Idle 'PAPER' pill when
no run armed; full green LIVE chip when active.
- index.css: lrchip motion keyframes (lrGlow/lrShimmer/lrBlink/lrBarFlow)
- also added the chip + endpoint-consumer to the legacy inline dashboard
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Files touched
M kalshi-dash/server.jsM kalshi-dash/src/Layout.jsxM kalshi-dash/src/index.css
Diff
commit a327393ff82239882cc28079fab23e4d250840d5
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Tue Jul 14 10:11:54 2026 -0700
Ken: live-run status chip in dashboard header (motion graphics)
- GET /api/live-run — day X/N, $ spent today vs daily cap, auto-off date, days left
- Layout.jsx status bar: animated LiveRunChip (pulsing glow, shimmer sweep, blinking
live dot, flowing progress bar, rAF count-up on $ spent). Idle 'PAPER' pill when
no run armed; full green LIVE chip when active.
- index.css: lrchip motion keyframes (lrGlow/lrShimmer/lrBlink/lrBarFlow)
- also added the chip + endpoint-consumer to the legacy inline dashboard
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---
kalshi-dash/server.js | 128 +++++++++++++++++++++++++++++++++++++++++++++
kalshi-dash/src/Layout.jsx | 62 +++++++++++++++++++++-
kalshi-dash/src/index.css | 26 +++++++++
3 files changed, 214 insertions(+), 2 deletions(-)
diff --git a/kalshi-dash/server.js b/kalshi-dash/server.js
index 6370473..384067c 100644
--- a/kalshi-dash/server.js
+++ b/kalshi-dash/server.js
@@ -710,6 +710,40 @@ const routes = {
}
},
+ // Live-run status for the header chip (bounded real-money run).
+ 'GET /api/live-run': async (req, res) => {
+ try {
+ const risk = await q('SELECT config FROM risk_state ORDER BY updated_at DESC LIMIT 1');
+ const safeMode = risk.rows[0]?.config?.safe_mode ?? true;
+ const lrRow = await kenQ("SELECT value FROM ken_config WHERE key = 'live_run'");
+ const lr = lrRow.rows[0]?.value || null;
+ if (!lr || !lr.active) {
+ return json(res, { active: false, safe_mode: safeMode });
+ }
+ const today = new Date().toISOString().split('T')[0];
+ const spentRow = await kenQ("SELECT COALESCE(SUM(cost_cents),0) t FROM ken_trades WHERE created_at::date = CURRENT_DATE AND action = 'buy'");
+ const spentToday = Number(spentRow.rows[0]?.t || 0);
+ // day index (1-based) within the run window
+ const msDay = 86400000;
+ const dayIdx = Math.min(lr.days, Math.max(1, Math.round((Date.parse(today) - Date.parse(lr.start_date)) / msDay) + 1));
+ const daysLeft = Math.max(0, Math.round((Date.parse(lr.end_date) - Date.parse(today)) / msDay));
+ json(res, {
+ active: true,
+ safe_mode: safeMode,
+ day: dayIdx,
+ days: lr.days,
+ days_left: daysLeft,
+ start_date: lr.start_date,
+ end_date: lr.end_date,
+ daily_budget_cents: lr.daily_budget_cents,
+ spent_today_cents: spentToday,
+ remaining_today_cents: Math.max(0, (lr.daily_budget_cents || 0) - spentToday),
+ });
+ } catch (err) {
+ json(res, { active: false, error: err.message }, 500);
+ }
+ },
+
// ════════════════════════════════════════
// KALSHI PROXY (all actions via POST)
// ════════════════════════════════════════
@@ -2954,6 +2988,34 @@ body{background:#000;overflow:hidden;font-family:'Segoe UI',system-ui,sans-serif
#c{position:fixed;top:0;left:0;z-index:0}
#hud{position:fixed;top:0;left:0;right:0;z-index:10;pointer-events:none}
.top-bar{display:flex;justify-content:center;align-items:center;padding:20px 40px;gap:30px}
+/* ── LIVE-RUN chip (motion graphics) ── */
+.lrchip{position:relative;display:inline-flex;align-items:center;gap:14px;margin:0 auto 20px;padding:11px 20px 11px 16px;border-radius:14px;
+ background:linear-gradient(120deg,rgba(16,185,129,.14),rgba(250,204,21,.10),rgba(16,185,129,.14));background-size:220% 100%;
+ border:1px solid rgba(16,185,129,.45);overflow:hidden;animation:lrGlow 2.4s ease-in-out infinite, lrSlide 6s linear infinite}
+.lrchip::after{content:"";position:absolute;top:0;left:-60%;width:45%;height:100%;
+ background:linear-gradient(100deg,transparent,rgba(255,255,255,.18),transparent);transform:skewX(-18deg);animation:lrShimmer 3.2s ease-in-out infinite}
+.lrchip.paper{background:rgba(148,163,184,.10);border-color:rgba(148,163,184,.28);animation:none}
+.lrchip.paper::after{display:none}
+.lr-live{display:flex;align-items:center;gap:7px;font-weight:800;font-size:12px;letter-spacing:1.5px;color:#34d399}
+.lr-dot{width:9px;height:9px;border-radius:50%;background:#34d399;box-shadow:0 0 0 0 rgba(52,211,153,.7);animation:lrBlink 1.3s ease-out infinite}
+.lrchip.paper .lr-live{color:#94a3b8}
+.lrchip.paper .lr-dot{background:#94a3b8;animation:none;box-shadow:none}
+.lr-day{font-size:12px;font-weight:700;color:var(--text);letter-spacing:.4px}
+.lr-day b{color:#facc15;font-size:15px}
+.lr-sep{width:1px;height:26px;background:rgba(255,255,255,.14)}
+.lr-spend{display:flex;flex-direction:column;gap:4px;min-width:150px}
+.lr-spend .lr-nums{display:flex;justify-content:space-between;font-size:11px;color:var(--muted);letter-spacing:.3px}
+.lr-spend .lr-nums b{color:#34d399;font-variant-numeric:tabular-nums}
+.lr-bar{height:6px;border-radius:6px;background:rgba(255,255,255,.10);overflow:hidden}
+.lr-bar>i{display:block;height:100%;border-radius:6px;background:linear-gradient(90deg,#22c55e,#facc15);background-size:200% 100%;
+ transition:width .9s cubic-bezier(.22,1,.36,1);animation:lrBarFlow 2.5s linear infinite}
+.lr-off{font-size:11px;color:var(--muted);letter-spacing:.3px;text-align:right}
+.lr-off b{color:var(--text)}
+@keyframes lrGlow{0%,100%{box-shadow:0 0 0 rgba(16,185,129,0),0 0 18px rgba(16,185,129,.18)}50%{box-shadow:0 0 22px rgba(16,185,129,.45),0 0 34px rgba(250,204,21,.18)}}
+@keyframes lrSlide{0%{background-position:0% 0}100%{background-position:220% 0}}
+@keyframes lrShimmer{0%{left:-60%}55%,100%{left:130%}}
+@keyframes lrBlink{0%{box-shadow:0 0 0 0 rgba(52,211,153,.7)}70%{box-shadow:0 0 0 8px rgba(52,211,153,0)}100%{box-shadow:0 0 0 0 rgba(52,211,153,0)}}
+@keyframes lrBarFlow{0%{background-position:0 0}100%{background-position:200% 0}}
.team-badge{display:flex;align-items:center;gap:12px;padding:10px 24px;border-radius:12px;pointer-events:auto;cursor:default}
.team-badge.alpha{background:rgba(99,102,241,.15);border:1px solid rgba(99,102,241,.4)}
.team-badge.beta{background:rgba(244,63,94,.15);border:1px solid rgba(244,63,94,.4)}
@@ -4664,6 +4726,67 @@ function MarketDataTab() {
</div>);
}
+// rAF count-up tween for a number (smooth motion on value changes)
+function useCountUp(target, dur = 900) {
+ const [val, setVal] = useState(target);
+ const from = useRef(target);
+ useEffect(() => {
+ const start = performance.now(), a = from.current, b = target;
+ let raf;
+ const tick = (now) => {
+ const t = Math.min(1, (now - start) / dur), e = 1 - Math.pow(1 - t, 3);
+ setVal(a + (b - a) * e);
+ if (t < 1) raf = requestAnimationFrame(tick); else from.current = b;
+ };
+ raf = requestAnimationFrame(tick);
+ return () => cancelAnimationFrame(raf);
+ }, [target]);
+ return val;
+}
+
+function LiveRunChip() {
+ const [lr, setLr] = useState(null);
+ useEffect(() => {
+ const load = () => api('/api/live-run').then(setLr).catch(() => {});
+ load();
+ const iv = setInterval(load, 7000);
+ return () => clearInterval(iv);
+ }, []);
+ const spentTarget = lr && lr.active ? lr.spent_today_cents : 0;
+ const spent = useCountUp(spentTarget);
+ if (!lr) return null;
+
+ if (!lr.active) {
+ // Not armed — subtle paper-mode pill so the state is always visible.
+ return (
+ <div className="lrchip paper" title="Real-money trading is OFF (safe/paper). Market data + paper portfolios only.">
+ <span className="lr-live"><span className="lr-dot" /> PAPER MODE</span>
+ <span className="lr-day">no live run armed</span>
+ </div>
+ );
+ }
+
+ const cap = lr.daily_budget_cents || 0;
+ const pct = cap > 0 ? Math.min(100, (lr.spent_today_cents / cap) * 100) : 0;
+ const fmtOff = (() => {
+ const [y, m, dd] = lr.end_date.split('-').map(Number);
+ return new Date(y, m - 1, dd).toLocaleDateString(undefined, { month: 'short', day: 'numeric' });
+ })();
+ return (
+ <div className="lrchip" title={'Bounded live run — $'+(cap/100).toFixed(0)+'/day, auto-off after '+lr.end_date}>
+ <span className="lr-live"><span className="lr-dot" /> LIVE</span>
+ <span className="lr-day">DAY <b>{lr.day}</b> / {lr.days}</span>
+ <span className="lr-sep" />
+ <div className="lr-spend">
+ <div className="lr-nums"><span>today</span><span><b>{'$'+(spent/100).toFixed(2)}</b> / {'$'+(cap/100).toFixed(0)}</span></div>
+ <div className="lr-bar"><i style={{ width: pct + '%' }} /></div>
+ </div>
+ <span className="lr-sep" />
+ <div className="lr-off">auto-off<br/><b>{fmtOff}</b> · {lr.days_left}d left</div>
+ </div>
+ );
+}
+
function App() {
const [data, setData] = useState(null);
const [expandedAgent, setExpandedAgent] = useState(null);
@@ -4713,6 +4836,10 @@ function App() {
<div className="ceo-tagline">Trading Intelligence Corporation</div>
</div>
+ <div style={{display:'flex',justifyContent:'center',marginBottom:8}}>
+ <LiveRunChip />
+ </div>
+
<div style={{display:'flex',justifyContent:'center',gap:8,marginBottom:24,flexWrap:'wrap'}}>
<button style={tabStyle('command')} onClick={()=>setTab('command')}>Command Center</button>
<button style={tabStyle('market')} onClick={()=>setTab('market')}>Market Data</button>
@@ -4790,6 +4917,7 @@ const server = http.createServer(async (req, res) => {
'/api/auth/google',
'/api/auth/google-config',
'/api/health',
+ '/api/live-run',
'/api/portfauxlio',
'/api/portfauxlio/charts',
'/api/portfauxlio/swot',
diff --git a/kalshi-dash/src/Layout.jsx b/kalshi-dash/src/Layout.jsx
index 81c76f4..a9250dd 100644
--- a/kalshi-dash/src/Layout.jsx
+++ b/kalshi-dash/src/Layout.jsx
@@ -1,8 +1,60 @@
-import React, { useState, useEffect } from 'react';
+import React, { useState, useEffect, useRef } from 'react';
import { Outlet, Link, useLocation, useNavigate } from 'react-router-dom';
import { api, checkSession, logout } from './api';
import TickerTape from './components/TickerTape';
+// rAF count-up tween so the $ spent value animates on change (motion graphics).
+function useCountUp(target, dur = 900) {
+ const [val, setVal] = useState(target);
+ const from = useRef(target);
+ useEffect(() => {
+ const start = performance.now(), a = from.current, b = target;
+ let raf;
+ const tick = (now) => {
+ const t = Math.min(1, (now - start) / dur), e = 1 - Math.pow(1 - t, 3);
+ setVal(a + (b - a) * e);
+ if (t < 1) raf = requestAnimationFrame(tick); else from.current = b;
+ };
+ raf = requestAnimationFrame(tick);
+ return () => cancelAnimationFrame(raf);
+ }, [target]);
+ return val;
+}
+
+// Header status-bar chip for the bounded real-money run.
+function LiveRunChip({ lr }) {
+ const spent = useCountUp(lr && lr.active ? lr.spent_today_cents : 0);
+ if (!lr) return null;
+
+ if (!lr.active) {
+ return (
+ <span className="lrchip idle" title="No bounded live run armed — safe/paper (market data + paper portfolios only).">
+ <span className="lr-dot" /> <span className="lr-live">PAPER</span>
+ <span className="lr-day" style={{ opacity: .8 }}>run idle</span>
+ </span>
+ );
+ }
+
+ const cap = lr.daily_budget_cents || 0;
+ const pct = cap > 0 ? Math.min(100, (lr.spent_today_cents / cap) * 100) : 0;
+ const off = (() => {
+ const [y, m, d] = String(lr.end_date).split('-').map(Number);
+ return new Date(y, m - 1, d).toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
+ })();
+ return (
+ <span className="lrchip" title={'Bounded live run — $' + (cap / 100).toFixed(0) + '/day, auto-off after ' + lr.end_date}>
+ <span className="lr-dot" /> <span className="lr-live">LIVE</span>
+ <span className="lr-pipe">·</span>
+ <span className="lr-day">DAY <b>{lr.day}</b>/{lr.days}</span>
+ <span className="lr-pipe">·</span>
+ <span className="lr-amt"><b>{'$' + (spent / 100).toFixed(2)}</b>/{'$' + (cap / 100).toFixed(0)}</span>
+ <span className="lr-bar"><i style={{ width: pct + '%' }} /></span>
+ <span className="lr-pipe">·</span>
+ <span className="lr-off">off <b>{off}</b> · {lr.days_left}d</span>
+ </span>
+ );
+}
+
const NAV = [
{ to: '/', label: 'Dashboard', d: 'M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6' },
{ to: '/markets', label: 'Markets', d: 'M13 7h8m0 0v8m0-8l-8 8-4-4-6 6' },
@@ -44,6 +96,7 @@ export default function Layout() {
const loc = useLocation();
const nav = useNavigate();
const [h, setH] = useState(null);
+ const [lr, setLr] = useState(null);
const [col, setCol] = useState(false);
const [authChecked, setAuthChecked] = useState(false);
@@ -51,7 +104,10 @@ export default function Layout() {
checkSession().then(ok => {
if (!ok) { nav('/login'); return; }
setAuthChecked(true);
- const load = () => api('/health').then(setH).catch(() => {});
+ const load = () => {
+ api('/health').then(setH).catch(() => {});
+ api('/live-run').then(setLr).catch(() => {});
+ };
load();
const i = setInterval(load, 15000);
return () => clearInterval(i);
@@ -327,6 +383,8 @@ export default function Layout() {
<span style={{ color: 'var(--orange)', fontWeight: 700 }}>SAFE MODE ON</span>
</>
)}
+ <span style={{ color: 'var(--border-light)' }}>|</span>
+ <LiveRunChip lr={lr} />
<span style={{ marginLeft: 'auto', display: 'flex', alignItems: 'center', gap: 12 }}>
<span style={{ color: 'var(--border-light)' }}>
{new Date().toLocaleTimeString('en-US', { timeZone: 'America/Los_Angeles', hour: '2-digit', minute: '2-digit', second: '2-digit' })} PT
diff --git a/kalshi-dash/src/index.css b/kalshi-dash/src/index.css
index 0aea79d..0670379 100644
--- a/kalshi-dash/src/index.css
+++ b/kalshi-dash/src/index.css
@@ -911,3 +911,29 @@ tbody tr:nth-child(even):hover td {
grid-template-columns: 1fr;
}
}
+
+/* ── LIVE-RUN chip (bounded real-money run) — motion graphics ── */
+.lrchip{position:relative;display:inline-flex;align-items:center;gap:10px;padding:4px 12px;border-radius:9px;overflow:hidden;
+ font-size:11px;letter-spacing:.04em;
+ background:linear-gradient(120deg,rgba(16,185,129,.16),rgba(250,204,21,.10),rgba(16,185,129,.16));background-size:220% 100%;
+ border:1px solid rgba(16,185,129,.5);animation:lrGlow 2.4s ease-in-out infinite,lrSlide 6s linear infinite}
+.lrchip::after{content:"";position:absolute;top:0;left:-60%;width:45%;height:100%;
+ background:linear-gradient(100deg,transparent,rgba(255,255,255,.20),transparent);transform:skewX(-18deg);animation:lrShimmer 3.2s ease-in-out infinite}
+.lrchip.idle{background:rgba(148,163,184,.12);border-color:rgba(148,163,184,.30);animation:none}
+.lrchip.idle::after{display:none}
+.lrchip .lr-dot{width:8px;height:8px;border-radius:50%;background:#34d399;box-shadow:0 0 0 0 rgba(52,211,153,.7);animation:lrBlink 1.3s ease-out infinite}
+.lrchip.idle .lr-dot{background:#94a3b8;animation:none;box-shadow:none}
+.lrchip .lr-live{font-weight:800;color:#34d399}
+.lrchip.idle .lr-live{color:#94a3b8}
+.lrchip .lr-day b{color:#facc15;font-weight:800}
+.lrchip .lr-pipe{color:rgba(255,255,255,.18)}
+.lrchip .lr-bar{width:70px;height:5px;border-radius:5px;background:rgba(255,255,255,.12);overflow:hidden;display:inline-block;vertical-align:middle}
+.lrchip .lr-bar>i{display:block;height:100%;border-radius:5px;background:linear-gradient(90deg,#22c55e,#facc15);background-size:200% 100%;
+ transition:width .9s cubic-bezier(.22,1,.36,1);animation:lrBarFlow 2.5s linear infinite}
+.lrchip .lr-amt b{color:#34d399;font-variant-numeric:tabular-nums}
+.lrchip .lr-off b{color:var(--text,#e5e7eb)}
+@keyframes lrGlow{0%,100%{box-shadow:0 0 0 rgba(16,185,129,0),0 0 14px rgba(16,185,129,.16)}50%{box-shadow:0 0 20px rgba(16,185,129,.42),0 0 30px rgba(250,204,21,.16)}}
+@keyframes lrSlide{0%{background-position:0 0}100%{background-position:220% 0}}
+@keyframes lrShimmer{0%{left:-60%}55%,100%{left:130%}}
+@keyframes lrBlink{0%{box-shadow:0 0 0 0 rgba(52,211,153,.7)}70%{box-shadow:0 0 0 7px rgba(52,211,153,0)}100%{box-shadow:0 0 0 0 rgba(52,211,153,0)}}
+@keyframes lrBarFlow{0%{background-position:0 0}100%{background-position:200% 0}}
← 1bdf579 Ken: bounded live-run controls — $/day spend cap + auto-off
·
back to Ken
·
Ken: fix Weather page 400 — server now handles 'get_data' ac 16c240e →