diff --git a/src/pages/index.astro b/src/pages/index.astro index b612273..b2a3a2d 100644 --- a/src/pages/index.astro +++ b/src/pages/index.astro @@ -10,10 +10,6 @@ let liveAgentsExact = '34,812'; let liveRequests = '5B+'; let growthPct = '+50%'; -// Chart - synthetic exponential curve, W1=0 → NOW=liveAgents. Always 10 ticks. -const LAUNCH_TS = new Date('2026-02-09T00:00:00Z').getTime() / 1000; -const TICKS = 10; - try { const res = await fetch('https://polo.pilotprotocol.network/api/stats', { headers: { 'User-Agent': 'pilotprotocol-web' }, @@ -64,82 +60,6 @@ try { } } } catch {} - -// weeks since launch (title + tick labels) -const weeksSinceLaunch = Math.max(1, Math.round((Date.now() / 1000 - LAUNCH_TS) / (7 * 86400))); - -// Build chart from real daily data points when available. -let chartPoints: { label: string; value: number }[] = []; -if (typeof s !== 'undefined' && Array.isArray(s.daily) && s.daily.length >= 2) { - const entries = s.daily; - for (let i = 0; i < entries.length; i++) { - const val = Number(entries[i]?.online_nodes ?? entries[i]?.total_nodes); - if (Number.isFinite(val) && val > 0) { - let label: string; - if (i === entries.length - 1) { - label = 'NOW'; - } else { - const d = new Date(Number(entries[i]?.ts) * 1000); - label = `${d.getMonth() + 1}/${d.getDate()}`; - } - chartPoints.push({ label, value: Math.round(val) }); - } - } -} -// Fallback: if no daily data, use synthetic exponential -if (chartPoints.length < 2) { - const finalVal = parseInt(liveAgentsExact.replace(/,/g, ''), 10) || 35000; - const EXP_K = 4.2; - chartPoints = []; - for (let i = 0; i < TICKS; i++) { - const t = i / (TICKS - 1); - const v = finalVal * (Math.exp(EXP_K * t) - 1) / (Math.exp(EXP_K) - 1); - let label: string; - if (i === TICKS - 1) { - label = 'NOW'; - } else { - const wk = 1 + Math.round(((weeksSinceLaunch - 1) * i) / (TICKS - 1)); - label = `W${wk}`; - } - chartPoints.push({ label, value: Math.round(v) }); - } -} - -function numberToWords(n: number): string { - const words = ['zero','one','two','three','four','five','six','seven','eight','nine','ten','eleven','twelve','thirteen','fourteen','fifteen','sixteen','seventeen','eighteen','nineteen','twenty']; - return words[n] || `${n}`; -} -const weeksLabel = numberToWords(weeksSinceLaunch); - -// build chart geometry -const svgW = 1200, svgH = 360; -const padL = 60, padR = 60, padT = 40, padB = 40; -const plotW = svgW - padL - padR; -const plotH = svgH - padT - padB; -const chartMax = Math.max(1, ...chartPoints.map(p => p.value)); -const xFor = (i: number) => padL + (i / (chartPoints.length - 1)) * plotW; -const yFor = (v: number) => padT + (1 - v / chartMax) * plotH; -const coords = chartPoints.map((p, i) => ({ x: xFor(i), y: yFor(p.value), ...p })); - -// Build polyline from real data points (straight segments between them). -let linePath = ''; -for (let i = 0; i < coords.length; i++) { - const c = coords[i]; - linePath += (i === 0 ? `M${c.x.toFixed(1)} ${c.y.toFixed(1)}` : ` L${c.x.toFixed(1)} ${c.y.toFixed(1)}`); -} -const areaPath = linePath - ? `${linePath} L ${(padL + plotW).toFixed(1)} ${(padT + plotH).toFixed(1)} L ${padL.toFixed(1)} ${(padT + plotH).toFixed(1)} Z` - : ''; - -function fmtAxis(v: number): string { - if (v >= 1_000_000) return `${(v / 1_000_000).toFixed(1).replace(/\.0$/, '')}M`; - if (v >= 1_000) return `${(v / 1000).toFixed(v >= 10_000 ? 0 : 1).replace(/\.0$/, '')}K`; - return `${Math.round(v)}`; -} -const yTicks = [1.0, 0.5, 0.2, 0.05].map(frac => { - const v = chartMax * frac; - return { label: fmtAxis(v), y: yFor(v) }; -}); --- @@ -543,46 +463,7 @@ const yTicks = [1.0, 0.5, 0.2, 0.05].map(frac => { -
-
-
-
Agents · weekly
-
The network, {liveAgents} agents in {weeksLabel} weeks
-
-
- Pilot agents -
-
- - - - - - - - - {yTicks.map(t => )} - - - - - {coords.map((c, i) => ( - - ))} - - - {coords.map((c, i) => ( - {c.label} - ))} - - - {yTicks.map(t => {t.label})} - - - {liveAgents} agents - - -
+ @@ -957,13 +838,6 @@ const yTicks = [1.0, 0.5, 0.2, 0.05].map(frac => { // Live stats refresh - fetches live stats on page load and every 60s so numbers // stay current between site deploys. Build-time values are the fallback. (function(){ - var LAUNCH_TS = new Date('2026-02-09T00:00:00Z').getTime() / 1000; - var TICKS = 10; - var SVG_W = 1200, PAD_L = 60, PAD_R = 60, PAD_T = 40, PAD_B = 40; - var SVG_H = 360; - var PLOT_W = SVG_W - PAD_L - PAD_R; - var PLOT_H = SVG_H - PAD_T - PAD_B; - var WORDS = ['zero','one','two','three','four','five','six','seven','eight','nine','ten','eleven','twelve','thirteen','fourteen','fifteen','sixteen','seventeen','eighteen','nineteen','twenty']; function fmtAgentsLong(n){ if (n >= 1_000_000) return '~' + (n / 1_000_000).toFixed(1).replace(/\.0$/, '') + 'M'; @@ -976,11 +850,6 @@ const yTicks = [1.0, 0.5, 0.2, 0.05].map(frac => { if (n >= 1_000_000) return Math.round(n / 1_000_000) + 'M+'; return String(n); } - function fmtAxis(v){ - if (v >= 1_000_000) return (v / 1_000_000).toFixed(1).replace(/\.0$/, '') + 'M'; - if (v >= 1_000) return (v / 1000).toFixed(v >= 10_000 ? 0 : 1).replace(/\.0$/, '') + 'K'; - return String(Math.round(v)); - } function growth7d(daily, fallbackTotal){ if (!Array.isArray(daily) || daily.length < 2) return null; var last = daily[daily.length - 1]; @@ -1004,94 +873,6 @@ const yTicks = [1.0, 0.5, 0.2, 0.05].map(frac => { function setAll(sel, value){ document.querySelectorAll(sel).forEach(function(el){ el.textContent = value; }); } - function rebuildChart(finalVal, dailyEntries){ - if (!Number.isFinite(finalVal) || finalVal <= 0) return; - var svg = document.querySelector('.chart-wrap svg'); - if (!svg) return; - - // Build chart from real daily data points. - var pts = []; - if (Array.isArray(dailyEntries) && dailyEntries.length >= 2) { - for (var i = 0; i < dailyEntries.length; i++) { - var e = dailyEntries[i]; - var val = Number((e && (e.online_nodes || e.total_nodes)) || 0); - if (!(Number.isFinite(val) && val > 0)) continue; - var label; - if (i === dailyEntries.length - 1) { - label = 'NOW'; - } else { - var dt = new Date(Number((e && e.ts) || 0) * 1000); - label = (dt.getMonth() + 1) + '/' + dt.getDate(); - } - pts.push({ label: label, value: Math.round(val) }); - } - } - // Fallback: synthetic exponential - if (pts.length < 2) { - var EXP_K = 4.2; - var weeksSinceLaunch = Math.max(1, Math.round((Date.now() / 1000 - LAUNCH_TS) / (7 * 86400))); - var expDenom = Math.exp(EXP_K) - 1; - pts = []; - for (var k = 0; k < TICKS; k++) { - var tt = k / (TICKS - 1); - var vv2 = finalVal * (Math.exp(EXP_K * tt) - 1) / expDenom; - var lbl = k === TICKS - 1 ? 'NOW' : ('W' + (1 + Math.round(((weeksSinceLaunch - 1) * k) / (TICKS - 1)))); - pts.push({ label: lbl, value: Math.round(vv2) }); - } - } - var chartMax = pts[pts.length - 1].value || 1; - var xFor = function(i){ return PAD_L + (i / (pts.length - 1)) * PLOT_W; }; - var yFor = function(v){ return PAD_T + (1 - v / chartMax) * PLOT_H; }; - var coords = pts.map(function(p, i){ return { x: xFor(i), y: yFor(p.value), label: p.label, value: p.value }; }); - - // Polyline through real data points. - var linePath = ''; - for (var j = 0; j < coords.length; j++) { - var c = coords[j]; - linePath += (j === 0 ? 'M' : ' L') + c.x.toFixed(1) + ' ' + c.y.toFixed(1); - } - var areaPath = linePath + ' L ' + (PAD_L + PLOT_W).toFixed(1) + ' ' + (PAD_T + PLOT_H).toFixed(1) + ' L ' + PAD_L.toFixed(1) + ' ' + (PAD_T + PLOT_H).toFixed(1) + ' Z'; - - var yTicks = [1.0, 0.5, 0.2, 0.05].map(function(frac){ - var v = chartMax * frac; - return { label: fmtAxis(v), y: yFor(v) }; - }); - - var grid = svg.querySelector('.chart-grid'); - if (grid) grid.innerHTML = yTicks.map(function(t){ return ''; }).join(''); - - var area = svg.querySelector('.chart-area'); - if (area) area.setAttribute('d', areaPath); - var line = svg.querySelector('.chart-line'); - if (line) line.setAttribute('d', linePath); - - var dots = svg.querySelector('.chart-dots'); - if (dots) dots.innerHTML = coords.map(function(c, i){ - var isLast = i === coords.length - 1; - return ''; - }).join(''); - - var labels = svg.querySelector('.chart-labels'); - if (labels) labels.innerHTML = coords.map(function(c, i){ - var anchor = i === 0 ? 'start' : (i === coords.length - 1 ? 'end' : 'middle'); - return ''+c.label+''; - }).join(''); - - var yaxis = svg.querySelector('.chart-yaxis'); - if (yaxis) yaxis.innerHTML = yTicks.map(function(t){ - return ''+t.label+''; - }).join(''); - - var finalDot = coords[coords.length - 1]; - var finalLabel = svg.querySelector('[data-live-chart-label]'); - if (finalLabel) { - finalLabel.setAttribute('x', String(finalDot.x - 10)); - finalLabel.setAttribute('y', String(finalDot.y - 14)); - } - - var weeksWord = WORDS[weeksSinceLaunch] || String(weeksSinceLaunch); - setAll('[data-live="weeks"]', weeksWord); - } function refresh(){ fetch('https://polo.pilotprotocol.network/api/stats', { cache: 'no-store' }) .then(function(r){ return r.ok ? r.json() : null; }) @@ -1103,10 +884,7 @@ const yTicks = [1.0, 0.5, 0.2, 0.05].map(frac => { var longStr = fmtAgentsLong(activeN); setAll('[data-live="agents-long"]', longStr); setAll('[data-live="agents-exact"]', activeN.toLocaleString('en-US')); - document.querySelectorAll('[data-live-chart-label]').forEach(function(el){ - el.textContent = longStr + ' agents'; - }); - rebuildChart(activeN, s.daily); + } if (typeof s.total_requests === 'number') { setAll('[data-live="requests"]', fmtRequests(s.total_requests)); diff --git a/src/styles/system.css b/src/styles/system.css index 8ffaad1..aa96d01 100644 --- a/src/styles/system.css +++ b/src/styles/system.css @@ -871,65 +871,6 @@ html.osi-modal-open { overflow: hidden; } } .stat .note { font-size: 13px; color: var(--ink-dim); line-height: 1.4; } -/* ============================================================ - CHART - ============================================================ */ -.chart-wrap { - border: 1px solid var(--line); - padding: 32px; - background: var(--bg-2); - position: relative; - margin-top: 48px; - color: var(--accent); -} -@media (max-width: 640px) { .chart-wrap { padding: 20px; } } -.chart-head { - display: flex; - justify-content: space-between; - align-items: flex-end; - margin-bottom: 28px; - flex-wrap: wrap; - gap: 16px; -} -.chart-head .num { margin-bottom: 8px; display: block; } -.chart-head .title { - font-size: 22px; - letter-spacing: -0.015em; - font-weight: 500; - color: var(--ink); - font-family: var(--sans); -} -@media (max-width: 640px) { .chart-head .title { font-size: 18px; } } -.chart-head .title .accent { color: var(--accent); } - -.chart-head .legend { - display: flex; - gap: 20px; - font-family: var(--mono); - font-size: 11px; - letter-spacing: 0.1em; - text-transform: uppercase; - color: var(--ink-dim); -} -.legend .swatch { - display: inline-block; - width: 10px; - height: 10px; - margin-right: 8px; - vertical-align: middle; -} -.sw-a { background: var(--accent); } -.sw-b { background: var(--ink-faint); } - -.chart-grid line { stroke: var(--line); stroke-width: 0.5; } -.chart-labels text { font-family: 'JetBrains Mono', monospace; font-size: 10px; fill: var(--ink-dim); } -.chart-yaxis text { font-family: 'JetBrains Mono', monospace; font-size: 10px; fill: var(--ink-dim); } -.chart-dashed { stroke: var(--ink-faint); stroke-width: 1; fill: none; stroke-dasharray: 4 6; } -.chart-area { fill: url(#area); } -.chart-line { stroke: var(--accent); stroke-width: 2; fill: none; } -.chart-dots circle { fill: var(--accent); } -.chart-dot-now { stroke: var(--bg); stroke-width: 2; } -.chart-final text { font-family: 'Inter Tight', sans-serif; fill: var(--ink); font-size: 14px; font-weight: 500; } /* ============================================================ PULL QUOTE