1(function () { 2 "use strict"; 3 4 // âââ Configuration âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 5 let TOKENS_PER_DAY = 60e12; // 60 trillion tokens/day 6 let BLENDED_COST_PER_M = 0.50; // $0.50 per 1M tokens 7 const YEAR_START = new Date("2026-01-01T00:00:00Z"); 8 const UPDATE_INTERVAL = 100; 9 const DATA_URL = "/data/token-monitor.json"; 10 const LINK_URL = "/analytics/"; 11 12 let tokensPerMs = TOKENS_PER_DAY / 86400 / 1000; 13 let dollarPerMs = (TOKENS_PER_DAY / 1e6 * BLENDED_COST_PER_M) / 86400 / 1000; 14 15 function applyConfig(data) { 16 if (data && data.tokensPerDay) { 17 TOKENS_PER_DAY = data.tokensPerDay; 18 tokensPerMs = TOKENS_PER_DAY / 86400 / 1000; 19 } 20 if (data && data.blendedCostPerM) { 21 BLENDED_COST_PER_M = data.blendedCostPerM; 22 } 23 dollarPerMs = (TOKENS_PER_DAY / 1e6 * BLENDED_COST_PER_M) / 86400 / 1000; 24 } 25 26 // âââ Helpers âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 27 function getTokensToday() { 28 const now = new Date(); 29 const midnight = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate())); 30 return (now - midnight) * tokensPerMs; 31 } 32 function getTokensYear() { 33 return (Date.now() - YEAR_START.getTime()) * tokensPerMs; 34 } 35 function getDollarToday() { 36 const now = new Date(); 37 const midnight = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate())); 38 return (now - midnight) * dollarPerMs; 39 } 40 function getDollarYear() { 41 return (Date.now() - YEAR_START.getTime()) * dollarPerMs; 42 } 43 44 function fmtTokens(n) { 45 if (n >= 1e15) return (n / 1e15).toFixed(2) + " Q"; 46 if (n >= 1e12) return (n / 1e12).toFixed(2) + " T"; 47 if (n >= 1e9) return (n / 1e9).toFixed(1) + " B"; 48 return Math.floor(n).toLocaleString("en-US"); 49 } 50 function fmtDollar(n) { 51 if (n >= 1e9) return "$" + (n / 1e9).toFixed(2) + "B"; 52 if (n >= 1e6) return "$" + (n / 1e6).toFixed(1) + "M"; 53 return "$" + Math.floor(n).toLocaleString("en-US"); 54 } 55 56 // âââ Styles ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 57 const STYLES = ` 58 @import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;600&display=swap'); 59 60 .tkb-banner { 61 display: flex; 62 align-items: center; 63 justify-content: space-between; 64 gap: 8px; 65 background: #141820; 66 border: 1px solid #252c33; 67 border-radius: 10px; 68 padding: 10px 20px; 69 margin-bottom: 16px; 70 cursor: pointer; 71 text-decoration: none; 72 transition: border-color 0.2s, background 0.2s; 73 min-height: 56px; 74 } 75 .tkb-banner:hover { 76 border-color: #00e5a0; 77 background: #181e28; 78 } 79 80 .tkb-left { 81 display: flex; 82 align-items: center; 83 gap: 10px; 84 flex-shrink: 0; 85 } 86 .tkb-live { 87 display: flex; 88 align-items: center; 89 gap: 5px; 90 } 91 .tkb-live-dot { 92 width: 6px; height: 6px; 93 border-radius: 50%; 94 background: #00e5a0; 95 animation: tkb-pulse 2s ease-in-out infinite; 96 } 97 @keyframes tkb-pulse { 98 0%, 100% { opacity: 1; transform: scale(1); } 99 50% { opacity: 0.4; transform: scale(1.5); } 100 } 101 .tkb-live-text { 102 font-size: 10px; 103 font-weight: 600; 104 text-transform: uppercase; 105 letter-spacing: 0.08em; 106 color: #00e5a0; 107 } 108 .tkb-label { 109 font-size: 10px; 110 font-weight: 500; 111 letter-spacing: 0.05em; 112 text-transform: uppercase; 113 color: #6b7a8d; 114 white-space: nowrap; 115 } 116 117 .tkb-counters { 118 display: flex; 119 align-items: center; 120 gap: 0; 121 flex: 1; 122 justify-content: center; 123 } 124 .tkb-counter-item { 125 display: flex; 126 flex-direction: column; 127 align-items: center; 128 padding: 0 16px; 129 border-right: 1px solid #252c33; 130 }
131 .tkb-counter-item:last-child { border-right: none; } 132 .tkb-counter-title { 133 font-size: 9px; 134 font-weight: 500; 135 text-transform: uppercase; 136 letter-spacing: 0.06em; 137 color: #6b7a8d; 138 margin-bottom: 2px; 139 white-space: nowrap; 140 } 141 .tkb-counter-value { 142 font-family: 'JetBrains Mono', monospace; 143 font-size: 16px; 144 font-weight: 600; 145 white-space: nowrap; 146 } 147 .tkb-counter-value.cyan { 148 color: #00d4ff; 149 text-shadow: 0 0 8px rgba(0,212,255,0.35); 150 } 151 .tkb-counter-value.green { 152 color: #00e5a0; 153 text-shadow: 0 0 8px rgba(0,229,160,0.3); 154 } 155 .tkb-counter-value.gold { 156 color: #ffd700; 157 text-shadow: 0 0 8px rgba(255,215,0,0.35); 158 } 159 160 .tkb-arrow { 161 font-size: 14px; 162 color: #6b7a8d; 163 flex-shrink: 0; 164 transition: transform 0.2s, color 0.2s; 165 } 166 .tkb-banner:hover .tkb-arrow { 167 color: #00e5a0; 168 transform: translateX(3px); 169 } 170 171 /* Responsive */ 172 @media (max-width: 900px) { 173 .tkb-label { display: none; } 174 .tkb-counter-item { padding: 0 10px; } 175 .tkb-counter-value { font-size: 14px; } 176 } 177 @media (max-width: 600px) { 178 .tkb-banner { flex-wrap: wrap; padding: 8px 12px; gap: 6px; } 179 .tkb-counters { flex-wrap: wrap; gap: 4px; } 180 .tkb-counter-item { border-right: none; padding: 2px 8px; } 181 .tkb-counter-value { font-size: 13px; } 182 .tkb-left { width: 100%; justify-content: space-between; } 183 .tkb-arrow { display: none; } 184 } 185 `; 186 187 // âââ Injection âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 188 function findTargetGrid() { 189 // Look for the main grid container on the first screen 190 // Strategy: find heading containing "ÐлавнÑе ÑобÑÑиÑ" or "Models Pulse" 191 // and walk up to the grid parent, then insert before it 192 const allEls = document.querySelectorAll("h2, h3, [class*='grid']"); 193 194 // Method 1: Find by heading text 195 for (const el of document.querySelectorAll("h2, h3, span, div")) { 196 const text = el.textContent.trim(); 197 if (text === "ÐлавнÑе ÑобÑÑиÑ" || text === "Key Events" || text === "Models Pulse") { 198 // Walk up to find a grid container 199 let parent = el.parentElement; 200 for (let i = 0; i < 8; i++) { 201 if (!parent) break; 202 if (parent.className && ( 203 parent.className.includes("grid-cols") || 204 parent.className.includes("grid ") 205 )) { 206 return parent; 207 } 208 parent = parent.parentElement; 209 } 210 } 211 } 212 213 // Method 2: Find any element with grid-cols-2 or grid-cols-3 class 214 const grids = document.querySelectorAll("[class*='grid-cols']"); 215 if (grids.length > 0) return grids[0]; 216 217 return null; 218 } 219 220 function inject() { 221 // Only inject on the main page 222 const path = window.location.pathname; 223 if (path !== "/" && path !== "/index.html" && path !== "") return; 224 225 // Don't double-inject 226 if (document.getElementById("tkb-banner")) return; 227 228 const grid = findTargetGrid(); 229 if (!grid) return; 230 231 // Inject styles 232 if (!document.getElementById("tkb-styles")) { 233 const styleEl = document.createElement("style"); 234 styleEl.id = "tkb-styles"; 235 styleEl.textContent = STYLES; 236 document.head.appendChild(styleEl); 237 } 238 239 // Create banner 240 const banner = document.createElement("a"); 241 banner.id = "tkb-banner"; 242 banner.className = "tkb-banner"; 243 banner.href = LINK_URL; 244 banner.innerHTML = ` 245 <div class="tkb-left"> 246 <div class="tkb-live"> 247 <div class="tkb-live-dot"></div> 248 <span class="tkb-live-text">Live</span> 249 </div> 250 <span class="tkb-label">Global AI Tokens</span> 251 </div> 252 <div class="tkb-counters"> 253 <div class="tkb-counter-item"> 254 <div class="tkb-counter-title">Today</div> 255 <div class="tkb-counter-value cyan" id="tkb-tokens-today">â</div> 256 </div> 257 <div class="tkb-counter-item"> 258 <div class="tkb-counter-title">This year</div> 259 <div class="tkb-counter-value green" id="tkb-tokens-year">â</div> 260 </div> 261 <div class="tkb-counter-item"> 262 <div class="tkb-counter-title">$ Today</div> 263 <div class="tkb-counter-value gold" id="tkb-spend-today">â</div> 264 </div> 265 <div class="tkb-counter-item"> 266 <div class="tkb-counter-title">$ This year</div> 267 <div class="tkb-counter-value gold" id="tkb-spend-year">â</div> 268 </div> 269 </div> 270 <div class="tkb-arrow">â</div> 271 `; 272 273 // Insert before the grid 274 grid.parentNode.insertBefore(banner, grid); 275 276 // Start counter animation 277 const elToday = document.getElementById("tkb-tokens-today"); 278 const elYear = document.getElementById("tkb-tokens-year"); 279 const elSpendToday = document.getElementById("tkb-spend-today"); 280 const elSpendYear = document.getElementById("tkb-spend-year"); 281
282 function update() { 283 elToday.textContent = fmtTokens(getTokensToday()); 284 elYear.textContent = fmtTokens(getTokensYear()); 285 elSpendToday.textContent = fmtDollar(getDollarToday()); 286 elSpendYear.textContent = fmtDollar(getDollarYear()); 287 } 288 update(); 289 setInterval(update, UPDATE_INTERVAL); 290 } 291 292 // âââ Bootstrap âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 293 function bootstrap() { 294 // Try to load dynamic config 295 fetch(DATA_URL).then(r => r.json()).then(data => { 296 applyConfig(data); 297 }).catch(() => {}).finally(() => { 298 waitAndInject(); 299 }); 300 } 301 302 function waitAndInject() { 303 // The SPA may not have rendered yet, use MutationObserver 304 if (findTargetGrid()) { 305 inject(); 306 return; 307 } 308 309 let attempts = 0; 310 const maxAttempts = 80; // 8 seconds max 311 const observer = new MutationObserver(() => { 312 attempts++; 313 if (findTargetGrid()) { 314 observer.disconnect(); 315 inject(); 316 } else if (attempts >= maxAttempts) { 317 observer.disconnect(); 318 } 319 }); 320 observer.observe(document.body, { childList: true, subtree: true }); 321 322 // Also try with a timer as fallback 323 const interval = setInterval(() => { 324 attempts++; 325 if (findTargetGrid()) { 326 clearInterval(interval); 327 inject(); 328 } else if (attempts >= maxAttempts) { 329 clearInterval(interval); 330 } 331 }, 100); 332 } 333 334 if (document.readyState === "loading") { 335 document.addEventListener("DOMContentLoaded", bootstrap); 336 } else { 337 bootstrap(); 338 } 339})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.