@krtclcdy/tersio 2.9.0 → 2.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +53 -36
- package/cli/banner.js +82 -0
- package/cli/dashboard.js +30 -10
- package/cli/install.js +3 -0
- package/cli/update.js +8 -0
- package/cli/usage.js +85 -22
- package/dashboard/app.js +801 -0
- package/dashboard/styles.css +157 -0
- package/dashboard/template.html +213 -0
- package/extensions/shared/carbon.js +88 -0
- package/extensions/shared/pricing.js +121 -0
- package/extensions/shared/rtk-gain.js +61 -0
- package/extensions/shared/usage-ledger.js +64 -37
- package/package.json +2 -3
- package/cli/dashboard.html +0 -780
- /package/{cli → dashboard}/brand.webp +0 -0
package/cli/dashboard.html
DELETED
|
@@ -1,780 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="utf-8">
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
-
<meta name="color-scheme" content="dark light">
|
|
7
|
-
<title>Tersio Gain Dashboard</title>
|
|
8
|
-
<link rel="icon" type="image/png" href="brand.webp">
|
|
9
|
-
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
|
|
10
|
-
<script src="https://unpkg.com/lucide@latest"></script>
|
|
11
|
-
<script src="https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/gsap.min.js"></script>
|
|
12
|
-
<script src="https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/ScrollTrigger.min.js"></script>
|
|
13
|
-
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource/outfit@5/index.css">
|
|
14
|
-
<style>
|
|
15
|
-
:root {
|
|
16
|
-
--bg: #f4f4f5; --panel: #ffffff; --ink: #18181b; --dim: #52525b;
|
|
17
|
-
--line: rgba(24, 24, 27, .12); --accent: #047857; --accent-soft: rgba(4, 120, 87, .12);
|
|
18
|
-
--track: rgba(24, 24, 27, .08); --grid: rgba(4, 120, 87, .07);
|
|
19
|
-
}
|
|
20
|
-
@media (prefers-color-scheme: dark) {
|
|
21
|
-
:root {
|
|
22
|
-
--bg: #09090b; --panel: #101014; --ink: #f4f4f5; --dim: #a1a1aa;
|
|
23
|
-
--line: rgba(255, 255, 255, .09); --accent: #34d399; --accent-soft: rgba(52, 211, 153, .1);
|
|
24
|
-
--track: rgba(255, 255, 255, .07); --grid: rgba(52, 211, 153, .05);
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
body { background: var(--bg); color: var(--ink); }
|
|
28
|
-
.display { font-family: "Outfit", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }
|
|
29
|
-
.bg-blueprint {
|
|
30
|
-
background-image: linear-gradient(var(--grid) 1px, transparent 1px),
|
|
31
|
-
linear-gradient(90deg, var(--grid) 1px, transparent 1px);
|
|
32
|
-
background-size: 44px 44px;
|
|
33
|
-
mask-image: radial-gradient(ellipse 90% 70% at 50% 0%, black 30%, transparent 75%);
|
|
34
|
-
}
|
|
35
|
-
.mono { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-variant-numeric: tabular-nums; }
|
|
36
|
-
.rowline { border-bottom: 1px solid var(--line); }
|
|
37
|
-
.pill { display: inline-flex; align-items: center; gap: 4px; border-radius: 999px; padding: 3px 10px; font-size: 11px; white-space: nowrap; }
|
|
38
|
-
.pill.good { background: var(--accent-soft); color: var(--accent); }
|
|
39
|
-
.pill.bad { background: rgba(248, 113, 113, .12); color: #f87171; }
|
|
40
|
-
.pill.flat { background: var(--track); color: var(--dim); }
|
|
41
|
-
.bar-fill { background: var(--accent); border-radius: 999px; transform-origin: left; }
|
|
42
|
-
.bar-track { background: var(--track); border-radius: 999px; }
|
|
43
|
-
.live-dot { background: var(--accent); box-shadow: 0 0 0 0 var(--accent-soft); animation: ping 2s cubic-bezier(0, 0, .2, 1) infinite; }
|
|
44
|
-
@keyframes ping { 75%, 100% { box-shadow: 0 0 0 9px transparent; } }
|
|
45
|
-
.rise { opacity: 0; transform: translateY(26px); }
|
|
46
|
-
.rise.in { opacity: 1; transform: none; transition: opacity .7s cubic-bezier(.16, 1, .3, 1), transform .7s cubic-bezier(.16, 1, .3, 1); }
|
|
47
|
-
.tilt { transition: transform .25s cubic-bezier(.16, 1, .3, 1), background .25s; }
|
|
48
|
-
.tilt:hover { transform: translateX(6px); background: var(--accent-soft); }
|
|
49
|
-
.stripcell { transition: background .25s; }
|
|
50
|
-
.stripcell:hover { background: var(--accent-soft); }
|
|
51
|
-
button, .seg button, .tilt, .modelcard, .mbar, .cell, .stripcell { cursor: pointer; }
|
|
52
|
-
.btn-push { transition: transform .12s, background .2s; }
|
|
53
|
-
.btn-push:hover { background: var(--accent-soft); }
|
|
54
|
-
.btn-push:active { transform: scale(.96); }
|
|
55
|
-
a.social { color: var(--ink); }
|
|
56
|
-
a.social:hover { color: var(--accent); }
|
|
57
|
-
.seg button { color: var(--dim); border-radius: 8px; transition: background .2s, color .2s; }
|
|
58
|
-
.seg button.on { background: var(--accent-soft); color: var(--ink); }
|
|
59
|
-
.seg button:not(.on):hover { color: var(--ink); }
|
|
60
|
-
.mbar { display: flex; flex-direction: column-reverse; border-radius: 6px 6px 2px 2px; overflow: hidden; min-height: 3px; background: var(--track); }
|
|
61
|
-
.mbar > span { display: block; width: 100%; }
|
|
62
|
-
.monogram { width: 34px; height: 34px; border-radius: 10px; display: grid; place-items: center; font-weight: 700; font-size: 13px; color: #fff; flex-shrink: 0; }
|
|
63
|
-
.monogram svg { width: 18px; height: 18px; fill: #fff; }
|
|
64
|
-
.monogram.sm svg { width: 14px; height: 14px; }
|
|
65
|
-
.modelcard { transition: transform .25s cubic-bezier(.16, 1, .3, 1), border-color .25s; }
|
|
66
|
-
.modelcard:hover { transform: translateY(-4px); border-color: var(--accent); }
|
|
67
|
-
.heatmonth { font-size: 10px; }
|
|
68
|
-
.dock {
|
|
69
|
-
position: sticky; top: 12px; z-index: 50;
|
|
70
|
-
background: color-mix(in srgb, var(--panel) 84%, transparent);
|
|
71
|
-
-webkit-backdrop-filter: blur(18px) saturate(160%); backdrop-filter: blur(18px) saturate(160%);
|
|
72
|
-
border: 1px solid var(--line); border-radius: 20px;
|
|
73
|
-
box-shadow: 0 12px 40px rgba(0, 0, 0, .25);
|
|
74
|
-
}
|
|
75
|
-
.dock img.brand { border-radius: 10px; }
|
|
76
|
-
.dock {
|
|
77
|
-
background:
|
|
78
|
-
linear-gradient(var(--panel), var(--panel)) padding-box,
|
|
79
|
-
linear-gradient(120deg, var(--accent-soft), var(--line) 30%, var(--line) 70%, var(--accent-soft)) border-box;
|
|
80
|
-
border: 1px solid transparent;
|
|
81
|
-
}
|
|
82
|
-
.dock .brand { box-shadow: 0 0 12px var(--accent-soft); }
|
|
83
|
-
#reload:hover { box-shadow: 0 0 14px var(--accent-soft); border-color: var(--accent); }
|
|
84
|
-
.spark { display: flex; align-items: flex-end; gap: 3px; }
|
|
85
|
-
.spark > span { flex: 1; min-width: 3px; border-radius: 2px 2px 0 0; background: var(--accent); opacity: .45; }
|
|
86
|
-
.spark > span.top { opacity: 1; box-shadow: 0 0 8px var(--accent-soft); }
|
|
87
|
-
@media (prefers-reduced-motion: reduce) {
|
|
88
|
-
.rise { opacity: 1; transform: none; }
|
|
89
|
-
.live-dot { animation: none; }
|
|
90
|
-
.tilt, .btn-push, .seg button { transition: none; }
|
|
91
|
-
}
|
|
92
|
-
</style>
|
|
93
|
-
</head>
|
|
94
|
-
<body class="min-h-[100dvh] antialiased">
|
|
95
|
-
<div class="bg-blueprint pointer-events-none fixed inset-0" aria-hidden="true"></div>
|
|
96
|
-
|
|
97
|
-
<main class="overflow-x-hidden w-full max-w-full">
|
|
98
|
-
<div class="relative mx-auto max-w-7xl px-4 sm:px-6 pb-16">
|
|
99
|
-
|
|
100
|
-
<!-- Floating dock navbar -->
|
|
101
|
-
<header class="dock mt-4 flex w-full items-center gap-2 px-3 py-2">
|
|
102
|
-
<a href="#" class="flex shrink-0 items-center gap-2.5 pl-1 pr-2" aria-label="Tersio gain home">
|
|
103
|
-
<img src="brand.webp" alt="Tersio" width="32" height="32" class="brand size-8 rounded-full">
|
|
104
|
-
<span class="leading-tight text-left">
|
|
105
|
-
<span class="block font-bold tracking-tight text-sm">TERSIO GAIN</span>
|
|
106
|
-
<span class="mono block text-[11px]" style="color: var(--dim)">live ledger</span>
|
|
107
|
-
</span>
|
|
108
|
-
</a>
|
|
109
|
-
<span class="mono hidden shrink-0 sm:flex items-center gap-2 text-xs px-2 ml-auto" style="color: var(--dim)">
|
|
110
|
-
<span class="live-dot inline-block size-2 rounded-full"></span>
|
|
111
|
-
<span id="clock">--:--</span>
|
|
112
|
-
</span>
|
|
113
|
-
<span class="hidden sm:block w-px h-5 shrink-0" style="background: var(--line)" aria-hidden="true"></span>
|
|
114
|
-
<button id="reload" class="btn-push mono flex shrink-0 items-center gap-2 text-xs pl-2.5 pr-3 py-2 rounded-xl" style="border: 1px solid var(--line); color: var(--ink)" aria-label="Reload data">
|
|
115
|
-
<i data-lucide="refresh-cw" class="size-3.5"></i><span class="hidden md:inline">Reload</span>
|
|
116
|
-
</button>
|
|
117
|
-
</header>
|
|
118
|
-
|
|
119
|
-
<!-- Hero: centered total -->
|
|
120
|
-
<section class="mx-auto max-w-6xl text-center pt-14 pb-10">
|
|
121
|
-
<p class="hero-in mono text-[11px] uppercase tracking-[0.22em] mb-4" style="color: var(--accent)">Live token feed</p>
|
|
122
|
-
<p class="hero-in mono font-bold tracking-tighter leading-none text-6xl md:text-7xl" id="total">0</p>
|
|
123
|
-
<p class="hero-in mono mt-4 text-sm max-w-[62ch] mx-auto" style="color: var(--dim)" id="herosub">session tokens parsed from OMP transcripts. Input, output, cache read, cache write, USD cost, CO2.</p>
|
|
124
|
-
</section>
|
|
125
|
-
|
|
126
|
-
<!-- Savings bento: big USD card + nested right rail -->
|
|
127
|
-
<section class="grid grid-cols-12 grid-flow-dense gap-3" aria-label="Savings">
|
|
128
|
-
<div class="rise col-span-12 lg:col-span-7 lg:row-span-2 rounded-xl p-6 flex flex-col justify-between" style="border: 1px solid var(--line); background: var(--panel)">
|
|
129
|
-
<div>
|
|
130
|
-
<div class="flex items-center justify-between gap-2">
|
|
131
|
-
<p class="mono text-[11px] uppercase tracking-[0.14em]" style="color: var(--dim)">usd cost</p>
|
|
132
|
-
<span class="pill good mono" id="usdDelta">—</span>
|
|
133
|
-
</div>
|
|
134
|
-
<p class="mono font-bold tracking-tighter leading-none mt-2 text-6xl lg:text-7xl" id="usd">-</p>
|
|
135
|
-
</div>
|
|
136
|
-
<div class="mt-4">
|
|
137
|
-
<div class="spark h-16" id="usdSpark"></div>
|
|
138
|
-
<div class="flex gap-4 mt-3 mono text-xs" style="color: var(--dim)">
|
|
139
|
-
<span>~ <span id="usdPerDay" style="color: var(--ink)">-</span>/day</span>
|
|
140
|
-
<span>top <span id="usdTopDay" style="color: var(--ink)">-</span></span>
|
|
141
|
-
<span>x<span id="usdLeverage" style="color: var(--accent)">-</span> cache leverage</span>
|
|
142
|
-
</div>
|
|
143
|
-
</div>
|
|
144
|
-
<p class="mono text-[11px] mt-3" style="color: var(--dim)" id="usdNote">per-model price table</p>
|
|
145
|
-
</div>
|
|
146
|
-
<div class="col-span-12 lg:col-span-5 grid grid-cols-2 grid-flow-dense gap-3">
|
|
147
|
-
<div class="rise col-span-2 rounded-xl p-5" style="border: 1px solid var(--line); background: var(--panel)">
|
|
148
|
-
<div class="flex items-center justify-between gap-2">
|
|
149
|
-
<div><p class="mono text-[11px] uppercase tracking-[0.14em]" style="color: var(--dim)">saved by cache</p>
|
|
150
|
-
<p class="mono font-bold text-3xl mt-1" style="color: var(--accent)" id="saved">-</p></div>
|
|
151
|
-
<span class="pill good mono" id="savedDelta">—</span>
|
|
152
|
-
</div>
|
|
153
|
-
<p class="mono text-[11px] mt-2" style="color: var(--dim)">est. vs full input price</p>
|
|
154
|
-
</div>
|
|
155
|
-
<div class="rise col-span-1 rounded-xl p-5" style="border: 1px solid var(--line); background: var(--panel)">
|
|
156
|
-
<div class="flex items-center justify-between gap-2">
|
|
157
|
-
<div><p class="mono text-[11px] uppercase tracking-[0.14em]" style="color: var(--dim)">co2</p>
|
|
158
|
-
<p class="mono font-bold text-3xl mt-1" id="co2">-</p>
|
|
159
|
-
<p class="mt-2"><span class="pill good mono" id="co2Delta">—</span></p></div>
|
|
160
|
-
</div>
|
|
161
|
-
<p class="mono text-[11px] mt-2" style="color: var(--dim)">est. from output</p>
|
|
162
|
-
</div>
|
|
163
|
-
<div class="rise col-span-1 rounded-xl p-5" style="border: 1px solid var(--line); background: var(--panel)">
|
|
164
|
-
<div class="flex items-center justify-between gap-2">
|
|
165
|
-
<div><p class="mono text-[11px] uppercase tracking-[0.14em]" style="color: var(--dim)">cache share</p>
|
|
166
|
-
<p class="mono font-bold text-3xl mt-1" id="cacheShare">-</p>
|
|
167
|
-
<p class="mt-2"><span class="pill good mono" id="cacheDelta">—</span></p></div>
|
|
168
|
-
</div>
|
|
169
|
-
<p class="mono text-[11px] mt-2" style="color: var(--dim)">of tokens cached</p>
|
|
170
|
-
</div>
|
|
171
|
-
</div>
|
|
172
|
-
</section>
|
|
173
|
-
|
|
174
|
-
<!-- Token strip: nonzero buckets only -->
|
|
175
|
-
<section class="rise mt-3 flex rounded-xl overflow-hidden" style="border: 1px solid var(--line); background: var(--panel)" id="tokens" aria-label="Tokens by bucket"></section>
|
|
176
|
-
|
|
177
|
-
<!-- Activity: contribution heatmap, GitHub style -->
|
|
178
|
-
<section id="activity" class="rise mt-8 rounded-xl p-5 overflow-hidden" style="border: 1px solid var(--line); background: var(--panel); scroll-margin-top: 90px" aria-label="Activity graph">
|
|
179
|
-
<div class="flex items-center gap-2 mb-1">
|
|
180
|
-
<i data-lucide="calendar-days" class="size-4" style="color: var(--accent)"></i>
|
|
181
|
-
<h2 class="display font-bold tracking-tight text-xl">Activity</h2>
|
|
182
|
-
<span class="mono text-xs ml-auto" style="color: var(--dim)" id="graphRange"></span>
|
|
183
|
-
</div>
|
|
184
|
-
<p class="mono text-xs mb-4" style="color: var(--dim)">daily session tokens / last 26 weeks / darker means more</p>
|
|
185
|
-
<div class="overflow-x-auto pb-1">
|
|
186
|
-
<div class="flex gap-[3px] w-max" id="graph"></div>
|
|
187
|
-
</div>
|
|
188
|
-
<div class="mono text-[11px] mt-3 flex items-center gap-1.5" style="color: var(--dim)">
|
|
189
|
-
<span>less</span>
|
|
190
|
-
<span class="cell"></span><span class="cell l1"></span><span class="cell l2"></span><span class="cell l3"></span><span class="cell l4"></span>
|
|
191
|
-
<span>more</span>
|
|
192
|
-
</div>
|
|
193
|
-
<div class="heatmonth mono mt-2 flex" style="color: var(--dim)" id="graphMonths"></div>
|
|
194
|
-
</section>
|
|
195
|
-
|
|
196
|
-
<!-- Top models: 3 cards -->
|
|
197
|
-
<section class="mt-8" aria-label="Top models">
|
|
198
|
-
<div class="flex items-center gap-2 mb-1">
|
|
199
|
-
<i data-lucide="cpu" class="size-4" style="color: var(--accent)"></i>
|
|
200
|
-
<h2 class="display font-bold tracking-tight text-xl">Top models</h2>
|
|
201
|
-
<span class="mono text-xs ml-auto" style="color: var(--dim)">usage across sessions</span>
|
|
202
|
-
</div>
|
|
203
|
-
<p class="mono text-xs mb-4" style="color: var(--dim)">week-over-week vs prior 7 days</p>
|
|
204
|
-
<div class="grid grid-cols-1 md:grid-cols-12 grid-flow-dense gap-3" id="modelCards"></div>
|
|
205
|
-
</section>
|
|
206
|
-
|
|
207
|
-
<!-- Models list: full width -->
|
|
208
|
-
<section class="rise mt-8" aria-label="All models">
|
|
209
|
-
<div class="flex items-center gap-2 mb-1">
|
|
210
|
-
<i data-lucide="layers" class="size-4" style="color: var(--accent)"></i>
|
|
211
|
-
<h2 class="display font-bold tracking-tight text-xl">Models</h2>
|
|
212
|
-
</div>
|
|
213
|
-
<p class="mono text-xs mb-4" style="color: var(--dim)">ranked by tokens / top 8</p>
|
|
214
|
-
<ol id="models" class="rounded-xl overflow-hidden" style="border: 1px solid var(--line); background: var(--panel)"></ol>
|
|
215
|
-
</section>
|
|
216
|
-
|
|
217
|
-
<!-- Top tools: full-width impact table -->
|
|
218
|
-
<section id="tools" class="rise mt-8 rounded-xl p-5 overflow-hidden" style="border: 1px solid var(--line); background: var(--panel); scroll-margin-top: 90px" aria-label="Top tools">
|
|
219
|
-
<div class="flex items-center gap-2 mb-1">
|
|
220
|
-
<i data-lucide="terminal" class="size-4" style="color: var(--accent)"></i>
|
|
221
|
-
<h2 class="display font-bold tracking-tight text-xl">Top tools</h2>
|
|
222
|
-
<span class="mono text-xs ml-auto" style="color: var(--dim)">tool calls in sessions</span>
|
|
223
|
-
</div>
|
|
224
|
-
<div class="overflow-x-auto">
|
|
225
|
-
<table class="w-full mono text-[13px]" id="topTable">
|
|
226
|
-
<thead>
|
|
227
|
-
<tr class="text-left text-[11px] uppercase tracking-[0.14em]" style="color: var(--dim)">
|
|
228
|
-
<th class="font-normal text-right pr-3 py-2 w-10">#</th>
|
|
229
|
-
<th class="font-normal py-2 pr-3">Tool</th>
|
|
230
|
-
<th class="font-normal text-right py-2 pr-3">Calls</th>
|
|
231
|
-
<th class="font-normal text-right py-2 pr-3">Share</th>
|
|
232
|
-
<th class="font-normal py-2 min-w-32">Impact</th>
|
|
233
|
-
</tr>
|
|
234
|
-
</thead>
|
|
235
|
-
<tbody id="top"></tbody>
|
|
236
|
-
</table>
|
|
237
|
-
</div>
|
|
238
|
-
<p id="emptyTop" class="mono text-sm py-6 text-center hidden" style="color: var(--dim)">No tool calls yet.</p>
|
|
239
|
-
</section>
|
|
240
|
-
|
|
241
|
-
<!-- Token activity: stacked per-model bars -->
|
|
242
|
-
<section id="modelactivity" class="rise mt-8 rounded-xl p-5" style="border: 1px solid var(--line); background: var(--panel); scroll-margin-top: 90px" aria-label="Token activity">
|
|
243
|
-
<div class="flex flex-wrap items-center gap-3 mb-1">
|
|
244
|
-
<i data-lucide="chart-column" class="size-4" style="color: var(--accent)"></i>
|
|
245
|
-
<h2 class="display font-bold tracking-tight text-xl">Token activity</h2>
|
|
246
|
-
<div class="seg mono text-xs flex items-center gap-1 ml-auto p-1 rounded-[10px]" style="border: 1px solid var(--line)" role="tablist" aria-label="Range">
|
|
247
|
-
<button data-range="daily" class="on px-3 py-1.5" role="tab">Daily</button>
|
|
248
|
-
<button data-range="weekly" class="px-3 py-1.5" role="tab">Weekly</button>
|
|
249
|
-
<button data-range="cumulative" class="px-3 py-1.5" role="tab">Cumulative</button>
|
|
250
|
-
</div>
|
|
251
|
-
</div>
|
|
252
|
-
<p class="mono text-xs mb-4" style="color: var(--dim)" id="rangeNote">stacked by model / last 60 days</p>
|
|
253
|
-
<div class="flex items-end gap-[3px] h-56" id="bars"></div>
|
|
254
|
-
<div class="flex gap-[3px] mt-2 mono text-[10px]" style="color: var(--dim)" id="barDays"></div>
|
|
255
|
-
<div class="flex flex-wrap gap-x-4 gap-y-1.5 mt-4 mono text-[11px]" style="color: var(--dim)" id="legend"></div>
|
|
256
|
-
</section>
|
|
257
|
-
|
|
258
|
-
<footer class="mono text-xs mt-12 pt-4" style="color: var(--dim); border-top: 1px solid var(--line)">
|
|
259
|
-
<div class="flex items-center gap-3">
|
|
260
|
-
<span>tersio gain dashboard / single file</span>
|
|
261
|
-
<span class="pill flat" id="verChip">tersio</span>
|
|
262
|
-
</div>
|
|
263
|
-
<div class="flex items-center gap-4 mt-3">
|
|
264
|
-
<span>session tokens stay local — parsed on this machine, never uploaded</span>
|
|
265
|
-
<span class="ml-auto flex items-center gap-1" style="color: var(--ink)">
|
|
266
|
-
<a href="https://github.com/KurutoDenzeru" target="_blank" rel="noopener" aria-label="GitHub" class="btn-push social grid size-8 place-items-center rounded-lg"><svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg></a>
|
|
267
|
-
<a href="https://linkedin.com/in/kurtcalacday/" target="_blank" rel="noopener" aria-label="LinkedIn" class="btn-push social grid size-8 place-items-center rounded-lg"><svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"/></svg></a>
|
|
268
|
-
<a href="https://instagram.com/krtclcdy/" target="_blank" rel="noopener" aria-label="Instagram" class="btn-push social grid size-8 place-items-center rounded-lg"><svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M7.0301.084c-1.2768.0602-2.1487.264-2.911.5634-.7888.3075-1.4575.72-2.1228 1.3877-.6652.6677-1.075 1.3368-1.3802 2.127-.2954.7638-.4956 1.6365-.552 2.914-.0564 1.2775-.0689 1.6882-.0626 4.947.0062 3.2586.0206 3.6671.0825 4.9473.061 1.2765.264 2.1482.5635 2.9107.308.7889.72 1.4573 1.388 2.1228.6679.6655 1.3365 1.0743 2.1285 1.38.7632.295 1.6361.4961 2.9134.552 1.2773.056 1.6884.069 4.9462.0627 3.2578-.0062 3.668-.0207 4.9478-.0814 1.28-.0607 2.147-.2652 2.9098-.5633.7889-.3086 1.4578-.72 2.1228-1.3881.665-.6682 1.0745-1.3378 1.3795-2.1284.2957-.7632.4966-1.636.552-2.9124.056-1.2809.0692-1.6898.063-4.948-.0063-3.2583-.021-3.6668-.0817-4.9465-.0607-1.2797-.264-2.1487-.5633-2.9117-.3084-.7889-.72-1.4568-1.3876-2.1228C21.2982 1.33 20.628.9208 19.8378.6165 19.074.321 18.2017.1197 16.9244.0645 15.6471.0093 15.236-.005 11.977.0014 8.718.0076 8.31.0215 7.0301.0839m.1402 21.6932c-1.17-.0509-1.8053-.2453-2.2287-.408-.5606-.216-.96-.4771-1.3819-.895-.422-.4178-.6811-.8186-.9-1.378-.1644-.4234-.3624-1.058-.4171-2.228-.0595-1.2645-.072-1.6442-.079-4.848-.007-3.2037.0053-3.583.0607-4.848.05-1.169.2456-1.805.408-2.2282.216-.5613.4762-.96.895-1.3816.4188-.4217.8184-.6814 1.3783-.9003.423-.1651 1.0575-.3614 2.227-.4171 1.2655-.06 1.6447-.072 4.848-.079 3.2033-.007 3.5835.005 4.8495.0608 1.169.0508 1.8053.2445 2.228.408.5608.216.96.4754 1.3816.895.4217.4194.6816.8176.9005 1.3787.1653.4217.3617 1.056.4169 2.2263.0602 1.2655.0739 1.645.0796 4.848.0058 3.203-.0055 3.5834-.061 4.848-.051 1.17-.245 1.8055-.408 2.2294-.216.5604-.4763.96-.8954 1.3814-.419.4215-.8181.6811-1.3783.9-.4224.1649-1.0577.3617-2.2262.4174-1.2656.0595-1.6448.072-4.8493.079-3.2045.007-3.5825-.006-4.848-.0608M16.953 5.5864A1.44 1.44 0 1 0 18.39 4.144a1.44 1.44 0 0 0-1.437 1.4424M5.8385 12.012c.0067 3.4032 2.7706 6.1557 6.173 6.1493 3.4026-.0065 6.157-2.7701 6.1506-6.1733-.0065-3.4032-2.771-6.1565-6.174-6.1498-3.403.0067-6.156 2.771-6.1496 6.1738M8 12.0077a4 4 0 1 1 4.008 3.9921A3.9996 3.9996 0 0 1 8 12.0077"/></svg></a>
|
|
269
|
-
</span>
|
|
270
|
-
<a id="rawlink" href="data.json" class="underline underline-offset-2">raw data.json</a>
|
|
271
|
-
</div>
|
|
272
|
-
</footer>
|
|
273
|
-
</div>
|
|
274
|
-
</main>
|
|
275
|
-
|
|
276
|
-
<script>
|
|
277
|
-
(function () {
|
|
278
|
-
var reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
279
|
-
var hasGsap = !reduce && typeof window.gsap !== 'undefined';
|
|
280
|
-
var hasST = hasGsap && typeof window.ScrollTrigger !== 'undefined';
|
|
281
|
-
if (hasST) gsap.registerPlugin(ScrollTrigger);
|
|
282
|
-
|
|
283
|
-
function fmt(n) { return Number(n).toLocaleString('en-US'); }
|
|
284
|
-
function fmtShort(n) {
|
|
285
|
-
if (n >= 1e12) return (n / 1e12).toFixed(1) + 'T';
|
|
286
|
-
if (n >= 1e9) return (n / 1e9).toFixed(1) + 'B';
|
|
287
|
-
if (n >= 1e6) return (n / 1e6).toFixed(1) + 'M';
|
|
288
|
-
if (n >= 1e3) return (n / 1e3).toFixed(1) + 'K';
|
|
289
|
-
return String(Math.round(n));
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
function countUp(el, target, money) {
|
|
293
|
-
function text(v) { return money ? '$' + v.toFixed(2) : fmt(Math.round(v)); }
|
|
294
|
-
if (!hasGsap || target <= 0) { el.textContent = text(target); return; }
|
|
295
|
-
var o = { v: 0 };
|
|
296
|
-
gsap.to(o, { v: target, duration: 1.1, ease: 'power3.out', onUpdate: function () { el.textContent = text(o.v); } });
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
function tick() {
|
|
300
|
-
var c = document.getElementById('clock');
|
|
301
|
-
if (c) c.textContent = new Date().toLocaleString('en-US', { month: 'short', day: 'numeric', hour: 'numeric', minute: '2-digit' });
|
|
302
|
-
}
|
|
303
|
-
tick(); setInterval(tick, 1000);
|
|
304
|
-
|
|
305
|
-
var VENDORS = [
|
|
306
|
-
[/glm|z-ai|zhipu/i, 'Zhipu', 'Z', '#3b82f6', ''],
|
|
307
|
-
[/muse/i, 'Meta', 'M', '#0082fb', 'M6.915 4.03c-1.968 0-3.683 1.28-4.871 3.113C.704 9.208 0 11.883 0 14.449c0 .706.07 1.369.21 1.973a6.624 6.624 0 0 0 .265.86 5.297 5.297 0 0 0 .371.761c.696 1.159 1.818 1.927 3.593 1.927 1.497 0 2.633-.671 3.965-2.444.76-1.012 1.144-1.626 2.663-4.32l.756-1.339.186-.325c.061.1.121.196.183.3l2.152 3.595c.724 1.21 1.665 2.556 2.47 3.314 1.046.987 1.992 1.22 3.06 1.22 1.075 0 1.876-.355 2.455-.843a3.743 3.743 0 0 0 .81-.973c.542-.939.861-2.127.861-3.745 0-2.72-.681-5.357-2.084-7.45-1.282-1.912-2.957-2.93-4.716-2.93-1.047 0-2.088.467-3.053 1.308-.652.57-1.257 1.29-1.82 2.05-.69-.875-1.335-1.547-1.958-2.056-1.182-.966-2.315-1.303-3.454-1.303zm10.16 2.053c1.147 0 2.188.758 2.992 1.999 1.132 1.748 1.647 4.195 1.647 6.4 0 1.548-.368 2.9-1.839 2.9-.58 0-1.027-.23-1.664-1.004-.496-.601-1.343-1.878-2.832-4.358l-.617-1.028a44.908 44.908 0 0 0-1.255-1.98c.07-.109.141-.224.211-.327 1.12-1.667 2.118-2.602 3.358-2.602zm-10.201.553c1.265 0 2.058.791 2.675 1.446.307.327.737.871 1.234 1.579l-1.02 1.566c-.757 1.163-1.882 3.017-2.837 4.338-1.191 1.649-1.81 1.817-2.486 1.817-.524 0-1.038-.237-1.383-.794-.263-.426-.464-1.13-.464-2.046 0-2.221.63-4.535 1.66-6.088.454-.687.964-1.226 1.533-1.533a2.264 2.264 0 0 1 1.088-.285z'],
|
|
308
|
-
[/qwen|qwq/i, 'Alibaba', 'Q', '#7c3aed', 'M23.919 14.545 20.817 9.17l1.47-2.544a.56.56 0 0 0 0-.566l-1.633-2.83a.57.57 0 0 0-.49-.283h-6.207L12.487.402a.57.57 0 0 0-.49-.284H8.732a.56.56 0 0 0-.49.284L5.139 5.775h-2.94a.56.56 0 0 0-.49.284L.077 8.887a.56.56 0 0 0 0 .567L3.18 14.83l-1.47 2.545a.56.56 0 0 0 0 .566l1.634 2.83a.57.57 0 0 0 .49.283h6.205l1.47 2.545a.57.57 0 0 0 .49.284h3.266a.57.57 0 0 0 .49-.284l3.104-5.375h2.94a.57.57 0 0 0 .49-.283l1.634-2.828a.55.55 0 0 0-.004-.568M8.733.686l1.634 2.828-1.634 2.828H21.8L20.164 9.17H7.425L5.63 6.06Zm1.306 19.801-6.205-.002 1.634-2.83h3.265L2.201 6.344h3.267q3.182 5.517 6.367 11.032zm10.124-5.66L18.53 12l-6.532 11.315-1.634-2.83c2.129-3.673 4.25-7.351 6.373-11.028h3.592l3.102 5.374z'],
|
|
309
|
-
[/deepseek/i, 'DeepSeek', 'D', '#4d6bfe', 'M23.748 4.651c-.254-.124-.364.113-.512.233-.051.04-.094.09-.137.137-.372.397-.806.657-1.373.626-.829-.046-1.537.214-2.163.848-.133-.782-.575-1.248-1.247-1.548-.352-.155-.708-.311-.955-.65-.172-.24-.219-.509-.305-.774-.055-.16-.11-.323-.293-.35-.2-.031-.278.136-.356.276-.313.572-.434 1.202-.422 1.84.027 1.436.633 2.58 1.838 3.393.137.094.172.187.129.323-.082.28-.18.553-.266.833-.055.179-.137.218-.328.14a5.5 5.5 0 0 1-1.737-1.179c-.857-.828-1.631-1.743-2.597-2.46a12 12 0 0 0-.689-.47c-.985-.957.13-1.743.387-1.836.27-.098.094-.433-.778-.428-.872.003-1.67.295-2.687.685a3 3 0 0 1-.465.136 9.6 9.6 0 0 0-2.883-.101c-1.885.21-3.39 1.1-4.497 2.622C.082 8.776-.231 10.854.152 13.02c.403 2.284 1.568 4.175 3.36 5.653 1.857 1.533 3.997 2.284 6.438 2.14 1.482-.085 3.132-.284 4.994-1.86.47.234.962.328 1.78.398.629.058 1.235-.031 1.705-.129.735-.155.684-.836.418-.961-2.155-1.004-1.682-.595-2.112-.926 1.095-1.295 2.768-3.598 3.284-6.733.05-.346.115-.834.108-1.114-.004-.171.035-.238.23-.257a4.2 4.2 0 0 0 1.545-.475c1.397-.763 1.96-2.016 2.093-3.517.02-.23-.004-.467-.247-.588M11.58 18.168c-2.088-1.642-3.101-2.183-3.52-2.16-.39.024-.32.472-.234.763.09.288.207.487.371.74.114.167.192.416-.113.603-.673.416-1.842-.14-1.897-.168-1.361-.801-2.5-1.86-3.301-3.306-.775-1.393-1.225-2.888-1.299-4.482-.02-.385.094-.522.477-.592a4.7 4.7 0 0 1 1.53-.038c2.131.311 3.946 1.264 5.467 2.774.868.86 1.525 1.887 2.202 2.89.72 1.066 1.494 2.082 2.48 2.915.348.291.626.513.892.677-.802.09-2.14.109-3.055-.615zm1.001-6.44a.306.306 0 0 1 .415-.287.3.3 0 0 1 .113.074.3.3 0 0 1 .086.214c0 .17-.136.307-.308.307a.303.303 0 0 1-.306-.307m3.11 1.596c-.2.081-.4.151-.591.16a1.25 1.25 0 0 1-.798-.254c-.274-.23-.47-.358-.551-.758a1.7 1.7 0 0 1 .015-.588c.07-.327-.007-.537-.238-.727-.188-.156-.426-.199-.689-.199a.6.6 0 0 1-.254-.078.253.253 0 0 1-.114-.358 1 1 0 0 1 .192-.21c.356-.202.767-.136 1.146.016.352.144.618.408 1.001.782.392.451.462.576.685.915.176.264.336.536.446.848.066.194-.02.353-.25.45'],
|
|
310
|
-
[/mimo/i, 'Xiaomi', 'Mi', '#ff6900', 'M12 0C8.016 0 4.756.255 2.493 2.516.23 4.776 0 8.033 0 12.012c0 3.98.23 7.235 2.494 9.497C4.757 23.77 8.017 24 12 24c3.983 0 7.243-.23 9.506-2.491C23.77 19.247 24 15.99 24 12.012c0-3.984-.233-7.243-2.502-9.504C19.234.252 15.978 0 12 0zM4.906 7.405h5.624c1.47 0 3.007.068 3.764.827.746.746.827 2.233.83 3.676v4.54a.15.15 0 0 1-.152.147h-1.947a.15.15 0 0 1-.152-.148V11.83c-.002-.806-.048-1.634-.464-2.051-.358-.36-1.026-.441-1.72-.458H7.158a.15.15 0 0 0-.151.147v6.98a.15.15 0 0 1-.152.148H4.906a.15.15 0 0 1-.15-.148V7.554a.15.15 0 0 1 .15-.149zm12.131 0h1.949a.15.15 0 0 1 .15.15v8.892a.15.15 0 0 1-.15.148h-1.949a.15.15 0 0 1-.151-.148V7.554a.15.15 0 0 1 .151-.149zM8.92 10.948h2.046c.083 0 .15.066.15.147v5.352a.15.15 0 0 1-.15.148H8.92a.15.15 0 0 1-.152-.148v-5.352a.15.15 0 0 1 .152-.147Z'],
|
|
311
|
-
[/kimi|moonshot/i, 'Moonshot', 'K', '#a855f7', ''],
|
|
312
|
-
[/minimax/i, 'MiniMax', 'M', '#e11d48', 'M11.43 3.92a.86.86 0 1 0-1.718 0v14.236a1.999 1.999 0 0 1-3.997 0V9.022a.86.86 0 1 0-1.718 0v3.87a1.999 1.999 0 0 1-3.997 0V11.49a.57.57 0 0 1 1.139 0v1.404a.86.86 0 0 0 1.719 0V9.022a1.999 1.999 0 0 1 3.997 0v9.134a.86.86 0 0 0 1.719 0V3.92a1.998 1.998 0 1 1 3.996 0v11.788a.57.57 0 1 1-1.139 0zm10.572 3.105a2 2 0 0 0-1.999 1.997v7.63a.86.86 0 0 1-1.718 0V3.923a1.999 1.999 0 0 0-3.997 0v16.16a.86.86 0 0 1-1.719 0V18.08a.57.57 0 1 0-1.138 0v2a1.998 1.998 0 0 0 3.996 0V3.92a.86.86 0 0 1 1.719 0v12.73a1.999 1.999 0 0 0 3.996 0V9.023a.86.86 0 1 1 1.72 0v6.686a.57.57 0 0 0 1.138 0V9.022a2 2 0 0 0-1.998-1.997'],
|
|
313
|
-
[/nemotron|nvidia/i, 'NVIDIA', 'N', '#76b900', 'M8.948 8.798v-1.43a6.7 6.7 0 0 1 .424-.018c3.922-.124 6.493 3.374 6.493 3.374s-2.774 3.851-5.75 3.851c-.398 0-.787-.062-1.158-.185v-4.346c1.528.185 1.837.857 2.747 2.385l2.04-1.714s-1.492-1.952-4-1.952a6.016 6.016 0 0 0-.796.035m0-4.735v2.138l.424-.027c5.45-.185 9.01 4.47 9.01 4.47s-4.08 4.964-8.33 4.964c-.37 0-.733-.035-1.095-.097v1.325c.3.035.61.062.91.062 3.957 0 6.82-2.023 9.593-4.408.459.371 2.34 1.263 2.73 1.652-2.633 2.208-8.772 3.984-12.253 3.984-.335 0-.653-.018-.971-.053v1.864H24V4.063zm0 10.326v1.131c-3.657-.654-4.673-4.46-4.673-4.46s1.758-1.944 4.673-2.262v1.237H8.94c-1.528-.186-2.73 1.245-2.73 1.245s.68 2.412 2.739 3.11M2.456 10.9s2.164-3.197 6.5-3.533V6.201C4.153 6.59 0 10.653 0 10.653s2.35 6.802 8.948 7.42v-1.237c-4.84-.6-6.492-5.936-6.492-5.936z'],
|
|
314
|
-
[/gpt|openai|codex/i, 'OpenAI', 'O', '#10a37f', 'M22.2819 9.8211a5.9847 5.9847 0 0 0-.5157-4.9108 6.0462 6.0462 0 0 0-6.5098-2.9A6.0651 6.0651 0 0 0 4.9807 4.1818a5.9847 5.9847 0 0 0-3.9977 2.9 6.0462 6.0462 0 0 0 .7427 7.0966 5.98 5.98 0 0 0 .511 4.9107 6.051 6.051 0 0 0 6.5146 2.9001A5.9847 5.9847 0 0 0 13.2599 24a6.0557 6.0557 0 0 0 5.7718-4.2058 5.9894 5.9894 0 0 0 3.9977-2.9001 6.0557 6.0557 0 0 0-.7475-7.0729zm-9.022 12.6081a4.4755 4.4755 0 0 1-2.8764-1.0408l.1419-.0804 4.7783-2.7582a.7948.7948 0 0 0 .3927-.6813v-6.7369l2.02 1.1686a.071.071 0 0 1 .038.052v5.5826a4.504 4.504 0 0 1-4.4945 4.4944zm-9.6607-4.1254a4.4708 4.4708 0 0 1-.5346-3.0137l.142.0852 4.783 2.7582a.7712.7712 0 0 0 .7806 0l5.8428-3.3685v2.3324a.0804.0804 0 0 1-.0332.0615L9.74 19.9502a4.4992 4.4992 0 0 1-6.1408-1.6464zM2.3408 7.8956a4.485 4.485 0 0 1 2.3655-1.9728V11.6a.7664.7664 0 0 0 .3879.6765l5.8144 3.3543-2.0201 1.1685a.0757.0757 0 0 1-.071 0l-4.8303-2.7865A4.504 4.504 0 0 1 2.3408 7.872zm16.5963 3.8558L13.1038 8.364 15.1192 7.2a.0757.0757 0 0 1 .071 0l4.8303 2.7913a4.4944 4.4944 0 0 1-.6765 8.1042v-5.6772a.79.79 0 0 0-.407-.667zm2.0107-3.0231l-.142-.0852-4.7735-2.7818a.7759.7759 0 0 0-.7854 0L9.409 9.2297V6.8974a.0662.0662 0 0 1 .0284-.0615l4.8303-2.7866a4.4992 4.4992 0 0 1 6.6802 4.66zM8.3065 12.863l-2.02-1.1638a.0804.0804 0 0 1-.038-.0567V6.0742a4.4992 4.4992 0 0 1 7.3757-3.4537l-.142.0805L8.704 5.459a.7948.7948 0 0 0-.3927.6813zm1.0976-2.3654l2.602-1.4998 2.6069 1.4998v2.9994l-2.5974 1.4997-2.6067-1.4997Z'],
|
|
315
|
-
[/claude|anthropic/i, 'Anthropic', 'A', '#d97757', 'M17.3041 3.541h-3.6718l6.696 16.918H24Zm-10.6082 0L0 20.459h3.7442l1.3693-3.5527h7.0052l1.3693 3.5528h3.7442L10.5363 3.5409Zm-.3712 10.2232 2.2914-5.9456 2.2914 5.9456Z'],
|
|
316
|
-
[/gemini/i, 'Google', 'G', '#4285f4', 'M11.04 19.32Q12 21.51 12 24q0-2.49.93-4.68.96-2.19 2.58-3.81t3.81-2.55Q21.51 12 24 12q-2.49 0-4.68-.93a12.3 12.3 0 0 1-3.81-2.58 12.3 12.3 0 0 1-2.58-3.81Q12 2.49 12 0q0 2.49-.96 4.68-.93 2.19-2.55 3.81a12.3 12.3 0 0 1-3.81 2.58Q2.49 12 0 12q2.49 0 4.68.96 2.19.93 3.81 2.55t2.55 3.81'],
|
|
317
|
-
[/google/i, 'Google', 'G', '#4285f4', 'M12.48 10.92v3.28h7.84c-.24 1.84-.853 3.187-1.787 4.133-1.147 1.147-2.933 2.4-6.053 2.4-4.827 0-8.6-3.893-8.6-8.72s3.773-8.72 8.6-8.72c2.6 0 4.507 1.027 5.907 2.347l2.307-2.307C18.747 1.44 16.133 0 12.48 0 5.867 0 .307 5.387.307 12s5.56 12 12.173 12c3.573 0 6.267-1.173 8.373-3.36 2.16-2.16 2.84-5.213 2.84-7.667 0-.76-.053-1.467-.173-2.053H12.48z'],
|
|
318
|
-
[/grok|xai/i, 'xAI', 'X', '#71717a', 'M14.234 10.162 22.977 0h-2.072l-7.591 8.824L7.251 0H.258l9.168 13.343L.258 24H2.33l8.016-9.318L16.749 24h6.993zm-2.837 3.299-.929-1.329L3.076 1.56h3.182l5.965 8.532.929 1.329 7.754 11.09h-3.182z'],
|
|
319
|
-
];
|
|
320
|
-
function vendorOf(model) {
|
|
321
|
-
for (var i = 0; i < VENDORS.length; i++) {
|
|
322
|
-
if (VENDORS[i][0].test(model)) return { name: VENDORS[i][1], mark: VENDORS[i][2], color: VENDORS[i][3], icon: VENDORS[i][4] };
|
|
323
|
-
}
|
|
324
|
-
return { name: 'Other', mark: (model || '?').charAt(0).toUpperCase(), color: '#71717a', icon: '' };
|
|
325
|
-
}
|
|
326
|
-
function badgeHTML(v, small) {
|
|
327
|
-
var inner = v.icon
|
|
328
|
-
? '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="' + v.icon + '"/></svg>'
|
|
329
|
-
: '<span>' + v.mark + '</span>';
|
|
330
|
-
return '<span class="monogram' + (small ? ' sm' : '') + '" style="background:' + v.color + '" title="' + v.name + '">' + inner + '</span>';
|
|
331
|
-
}
|
|
332
|
-
var PALETTE = ['#34d399', '#818cf8', '#22d3ee', '#fbbf24', '#f472b6', '#a78bfa', '#fb923c', '#2dd4bf'];
|
|
333
|
-
|
|
334
|
-
function dayKey(d) {
|
|
335
|
-
return d.getFullYear() + '-' + String(d.getMonth() + 1).padStart(2, '0') + '-' + String(d.getDate()).padStart(2, '0');
|
|
336
|
-
}
|
|
337
|
-
function modelTotal(byModel, m) {
|
|
338
|
-
var b = byModel[m] || { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
|
|
339
|
-
return b.input + b.output + b.cacheRead + b.cacheWrite;
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
var DATA = null, RANGE = 'daily', cardsShown = false;
|
|
343
|
-
|
|
344
|
-
function topModels(byModel, n) {
|
|
345
|
-
return Object.keys(byModel).sort(function (a, b) { return modelTotal(byModel, b) - modelTotal(byModel, a); }).slice(0, n);
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
function allDays() {
|
|
349
|
-
var set = {};
|
|
350
|
-
Object.keys(DATA.byDay || {}).forEach(function (d) { set[d] = 1; });
|
|
351
|
-
Object.keys(DATA.byDayModel || {}).forEach(function (d) { set[d] = 1; });
|
|
352
|
-
return Object.keys(set).sort();
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
function renderGraph(byDay) {
|
|
356
|
-
var g = document.getElementById('graph');
|
|
357
|
-
g.innerHTML = '';
|
|
358
|
-
var today = new Date(); today.setHours(0, 0, 0, 0);
|
|
359
|
-
var start = new Date(today); start.setDate(start.getDate() - (25 * 7) - today.getDay());
|
|
360
|
-
var max = 1, sums = {};
|
|
361
|
-
Object.keys(byDay).forEach(function (day) {
|
|
362
|
-
var b = byDay[day];
|
|
363
|
-
sums[day] = b.input + b.output + b.cacheRead + b.cacheWrite;
|
|
364
|
-
if (sums[day] > max) max = sums[day];
|
|
365
|
-
});
|
|
366
|
-
var months = document.getElementById('graphMonths');
|
|
367
|
-
months.innerHTML = '';
|
|
368
|
-
var lastMonth = '';
|
|
369
|
-
for (var w = 0; w < 26; w++) {
|
|
370
|
-
var col = document.createElement('div');
|
|
371
|
-
col.style.cssText = 'display:flex;flex-direction:column;gap:3px;';
|
|
372
|
-
var ml = document.createElement('span');
|
|
373
|
-
ml.className = 'heatmonth w-full truncate';
|
|
374
|
-
for (var i = 0; i < 7; i++) {
|
|
375
|
-
var dt = new Date(start); dt.setDate(dt.getDate() + w * 7 + i);
|
|
376
|
-
var key = dayKey(dt), v = sums[key] || 0, lvl = 0;
|
|
377
|
-
if (v > 0) lvl = Math.min(4, 1 + Math.floor((v / max) * 3.99));
|
|
378
|
-
var cell = document.createElement('span');
|
|
379
|
-
cell.className = 'cell' + (lvl ? ' l' + lvl : '');
|
|
380
|
-
cell.title = key + ': ' + fmt(v) + ' tokens';
|
|
381
|
-
col.appendChild(cell);
|
|
382
|
-
if (i === 0) {
|
|
383
|
-
var mm = dt.toLocaleString('en-US', { month: 'short' });
|
|
384
|
-
ml.textContent = mm !== lastMonth ? mm : '';
|
|
385
|
-
lastMonth = mm;
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
g.appendChild(col);
|
|
389
|
-
months.appendChild(ml);
|
|
390
|
-
}
|
|
391
|
-
// Month labels share the column grid.
|
|
392
|
-
months.className = 'heatmonth mono mt-2 flex';
|
|
393
|
-
months.style.cssText = 'display:flex;gap:3px;';
|
|
394
|
-
Array.prototype.forEach.call(months.children, function (m) { m.className += ' flex-1'; });
|
|
395
|
-
document.getElementById('graphRange').textContent =
|
|
396
|
-
dayKey(start) + ' to ' + dayKey(today);
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
// Buckets: [{ key, label, total, segs: [[model, v]] }]
|
|
400
|
-
function buckets() {
|
|
401
|
-
var days = allDays(), tops = topModels(DATA.byModel || {}, 8), out = [];
|
|
402
|
-
if (RANGE === 'daily') {
|
|
403
|
-
var last = days.slice(-60);
|
|
404
|
-
last.forEach(function (d) {
|
|
405
|
-
var per = (DATA.byDayModel || {})[d] || {}, segs = [], total = 0, other = 0;
|
|
406
|
-
tops.forEach(function (m) { var v = per[m] || 0; if (v) { segs.push([m, v]); total += v; } });
|
|
407
|
-
Object.keys(per).forEach(function (m) { if (tops.indexOf(m) < 0) other += per[m]; });
|
|
408
|
-
if (other) { segs.push(['other', other]); total += other; }
|
|
409
|
-
var dt = new Date(d + 'T12:00:00');
|
|
410
|
-
out.push({ key: d, label: isNaN(dt) ? '' : String(dt.getDate()), total: total, segs: segs });
|
|
411
|
-
});
|
|
412
|
-
document.getElementById('rangeNote').textContent = 'stacked by model / last 60 days';
|
|
413
|
-
} else if (RANGE === 'weekly') {
|
|
414
|
-
var weeks = {}, order = [];
|
|
415
|
-
days.forEach(function (d) {
|
|
416
|
-
var dt = new Date(d + 'T12:00:00'); if (isNaN(dt)) return;
|
|
417
|
-
var monday = new Date(dt); monday.setDate(dt.getDate() - ((dt.getDay() + 6) % 7));
|
|
418
|
-
var k = dayKey(monday);
|
|
419
|
-
if (!weeks[k]) { weeks[k] = {}; order.push(k); }
|
|
420
|
-
var per = (DATA.byDayModel || {})[d] || {};
|
|
421
|
-
Object.keys(per).forEach(function (m) { weeks[k][m] = (weeks[k][m] || 0) + per[m]; });
|
|
422
|
-
});
|
|
423
|
-
order.slice(-26).forEach(function (k) {
|
|
424
|
-
var per = weeks[k], segs = [], total = 0, other = 0;
|
|
425
|
-
tops.forEach(function (m) { var v = per[m] || 0; if (v) { segs.push([m, v]); total += v; } });
|
|
426
|
-
Object.keys(per).forEach(function (m) { if (tops.indexOf(m) < 0) other += per[m]; });
|
|
427
|
-
if (other) { segs.push(['other', other]); total += other; }
|
|
428
|
-
var dt = new Date(k + 'T12:00:00');
|
|
429
|
-
out.push({ key: k, label: isNaN(dt) ? '' : (dt.toLocaleString('en-US', { month: 'short' }) + ' ' + dt.getDate()), total: total, segs: segs });
|
|
430
|
-
});
|
|
431
|
-
document.getElementById('rangeNote').textContent = 'stacked by model / last 26 weeks';
|
|
432
|
-
} else {
|
|
433
|
-
var run = {}, cum = 0;
|
|
434
|
-
days.forEach(function (d) {
|
|
435
|
-
var per = (DATA.byDayModel || {})[d] || {}, segs = [], other = 0;
|
|
436
|
-
tops.forEach(function (m) {
|
|
437
|
-
var v = per[m] || 0;
|
|
438
|
-
run[m] = (run[m] || 0) + v;
|
|
439
|
-
if (run[m]) segs.push([m, run[m]]);
|
|
440
|
-
});
|
|
441
|
-
Object.keys(per).forEach(function (m) { if (tops.indexOf(m) < 0) { run[m] = (run[m] || 0) + per[m]; } });
|
|
442
|
-
other = Object.keys(run).reduce(function (a, m) { return tops.indexOf(m) < 0 ? a + run[m] : a; }, 0);
|
|
443
|
-
var tot = segs.reduce(function (a, s) { return a + s[1]; }, 0) + other;
|
|
444
|
-
if (other) segs.push(['other', other]);
|
|
445
|
-
cum = tot;
|
|
446
|
-
var dt = new Date(d + 'T12:00:00');
|
|
447
|
-
out.push({ key: d, label: isNaN(dt) ? '' : String(dt.getDate()), total: tot, segs: segs });
|
|
448
|
-
});
|
|
449
|
-
out = out.slice(-60);
|
|
450
|
-
document.getElementById('rangeNote').textContent = 'running total by model / all time';
|
|
451
|
-
}
|
|
452
|
-
return { list: out, tops: tops };
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
function renderBars() {
|
|
456
|
-
var b = buckets(), wrap = document.getElementById('bars'), axis = document.getElementById('barDays');
|
|
457
|
-
wrap.innerHTML = ''; axis.innerHTML = '';
|
|
458
|
-
var max = 1;
|
|
459
|
-
b.list.forEach(function (r) { max = Math.max(max, r.total); });
|
|
460
|
-
b.list.forEach(function (r) {
|
|
461
|
-
var col = document.createElement('div');
|
|
462
|
-
col.className = 'flex-1 flex items-end h-full';
|
|
463
|
-
col.title = r.key + ': ' + fmt(r.total) + ' tokens' +
|
|
464
|
-
(r.segs.length ? '\n' + r.segs.map(function (s) { return s[0] + ' ' + fmt(s[1]); }).join('\n') : '');
|
|
465
|
-
var bar = document.createElement('div');
|
|
466
|
-
bar.className = 'mbar w-full';
|
|
467
|
-
bar.style.height = Math.max(2, Math.round(r.total / max * 100)) + '%';
|
|
468
|
-
var order = r.segs.slice().sort(function (x, y) { return y[1] - x[1]; });
|
|
469
|
-
order.forEach(function (s) {
|
|
470
|
-
var seg = document.createElement('span');
|
|
471
|
-
var ci = s[0] === 'other' ? -1 : b.tops.indexOf(s[0]);
|
|
472
|
-
seg.style.background = ci < 0 ? 'var(--dim)' : PALETTE[ci % PALETTE.length];
|
|
473
|
-
seg.style.height = Math.max(1, Math.round(s[1] / r.total * 100)) + '%';
|
|
474
|
-
bar.appendChild(seg);
|
|
475
|
-
});
|
|
476
|
-
col.appendChild(bar); wrap.appendChild(col);
|
|
477
|
-
var lb = document.createElement('span');
|
|
478
|
-
lb.className = 'flex-1 text-center truncate'; lb.textContent = r.label;
|
|
479
|
-
axis.appendChild(lb);
|
|
480
|
-
});
|
|
481
|
-
var legend = document.getElementById('legend');
|
|
482
|
-
legend.innerHTML = '';
|
|
483
|
-
b.tops.slice(0, 8).forEach(function (m, i) {
|
|
484
|
-
var s = document.createElement('span');
|
|
485
|
-
s.className = 'flex items-center gap-1.5';
|
|
486
|
-
var dot = document.createElement('span');
|
|
487
|
-
dot.style.cssText = 'width:9px;height:9px;border-radius:3px;background:' + PALETTE[i % PALETTE.length] + ';display:inline-block';
|
|
488
|
-
s.appendChild(dot);
|
|
489
|
-
s.appendChild(document.createTextNode(m));
|
|
490
|
-
legend.appendChild(s);
|
|
491
|
-
});
|
|
492
|
-
if (hasGsap) {
|
|
493
|
-
gsap.from('#bars > div', { scaleY: 0.2, opacity: 0.2, transformOrigin: 'bottom', duration: 0.5, ease: 'power2.out', stagger: 0.008, overwrite: true });
|
|
494
|
-
if (hasST && !cardsShown) {
|
|
495
|
-
cardsShown = true;
|
|
496
|
-
gsap.from('#modelCards > div', {
|
|
497
|
-
scale: 0.92, opacity: 0.2, duration: 0.6, ease: 'power2.out', stagger: 0.08, overwrite: true,
|
|
498
|
-
scrollTrigger: { trigger: '#modelCards', start: 'top 88%' }
|
|
499
|
-
});
|
|
500
|
-
}
|
|
501
|
-
}
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
function wow(model) {
|
|
505
|
-
var days = allDays().slice(-14), cur = 0, prev = 0;
|
|
506
|
-
days.forEach(function (d, i) {
|
|
507
|
-
var v = ((DATA.byDayModel || {})[d] || {})[model] || 0;
|
|
508
|
-
if (i < days.length - 7) prev += v; else cur += v;
|
|
509
|
-
});
|
|
510
|
-
if (!prev) return cur ? 'new' : '—';
|
|
511
|
-
var p = Math.round((cur - prev) / prev * 100);
|
|
512
|
-
return (p >= 0 ? '+' : '') + p + '%';
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
function renderModels() {
|
|
516
|
-
var byModel = DATA.byModel || {}, tops = topModels(byModel, 8);
|
|
517
|
-
var cards = document.getElementById('modelCards');
|
|
518
|
-
cards.innerHTML = '';
|
|
519
|
-
tops.slice(0, 3).forEach(function (m, i) {
|
|
520
|
-
var v = vendorOf(m), d = wow(m);
|
|
521
|
-
var card = document.createElement('div');
|
|
522
|
-
card.className = 'modelcard md:col-span-4 rounded-xl p-5 overflow-hidden relative';
|
|
523
|
-
card.style.cssText = 'border: 1px solid var(--line); background: var(--panel)';
|
|
524
|
-
var ghost = document.createElement('span');
|
|
525
|
-
ghost.className = 'display pointer-events-none select-none absolute -right-2 -bottom-7 font-bold';
|
|
526
|
-
ghost.style.cssText = 'font-size: 120px; line-height: 1; opacity: .07';
|
|
527
|
-
ghost.textContent = v.mark;
|
|
528
|
-
var head = document.createElement('p');
|
|
529
|
-
head.className = 'mono text-xs mb-3'; head.style.color = 'var(--dim)';
|
|
530
|
-
head.textContent = '0' + (i + 1);
|
|
531
|
-
var row = document.createElement('div');
|
|
532
|
-
row.className = 'flex items-center gap-3';
|
|
533
|
-
var badgeWrap = document.createElement('span');
|
|
534
|
-
badgeWrap.innerHTML = badgeHTML(v, false);
|
|
535
|
-
var badge = badgeWrap.firstChild;
|
|
536
|
-
var mid = document.createElement('div'); mid.className = 'min-w-0 flex-1';
|
|
537
|
-
var nm = document.createElement('p'); nm.className = 'mono text-sm font-bold truncate'; nm.textContent = m;
|
|
538
|
-
var vn = document.createElement('p'); vn.className = 'mono text-xs truncate'; vn.style.color = 'var(--dim)'; vn.textContent = v.name;
|
|
539
|
-
mid.appendChild(nm); mid.appendChild(vn);
|
|
540
|
-
var tot = document.createElement('p'); tot.className = 'mono font-bold text-lg shrink-0'; tot.textContent = fmtShort(modelTotal(byModel, m));
|
|
541
|
-
row.appendChild(badge); row.appendChild(mid); row.appendChild(tot);
|
|
542
|
-
var delta = document.createElement('p');
|
|
543
|
-
delta.className = 'mono text-xs mt-3';
|
|
544
|
-
delta.style.color = d.charAt(0) === '-' ? '#f87171' : 'var(--accent)';
|
|
545
|
-
delta.textContent = d;
|
|
546
|
-
card.appendChild(ghost); card.appendChild(head); card.appendChild(row); card.appendChild(delta);
|
|
547
|
-
cards.appendChild(card);
|
|
548
|
-
});
|
|
549
|
-
var ol = document.getElementById('models');
|
|
550
|
-
ol.innerHTML = '';
|
|
551
|
-
var max = 1;
|
|
552
|
-
tops.forEach(function (m) { max = Math.max(max, modelTotal(byModel, m)); });
|
|
553
|
-
tops.forEach(function (m, i) {
|
|
554
|
-
var v = vendorOf(m);
|
|
555
|
-
var li = document.createElement('li');
|
|
556
|
-
li.className = 'tilt flex items-center gap-3 px-4 py-3' + (i < tops.length - 1 ? ' rowline' : '');
|
|
557
|
-
var badgeWrap = document.createElement('span');
|
|
558
|
-
badgeWrap.innerHTML = badgeHTML(v, true);
|
|
559
|
-
var badge = badgeWrap.firstChild;
|
|
560
|
-
badge.style.cssText += 'width:28px;height:28px;font-size:11px;border-radius:8px;';
|
|
561
|
-
var mid = document.createElement('div'); mid.className = 'min-w-0 flex-1';
|
|
562
|
-
var nm = document.createElement('p'); nm.className = 'mono text-sm truncate'; nm.textContent = m;
|
|
563
|
-
var track = document.createElement('div'); track.className = 'bar-track mt-1.5 h-1.5 overflow-hidden';
|
|
564
|
-
var fill = document.createElement('div'); fill.className = 'bar-fill h-full';
|
|
565
|
-
fill.style.width = Math.max(2, Math.round(modelTotal(byModel, m) / max * 100)) + '%';
|
|
566
|
-
track.appendChild(fill); mid.appendChild(nm); mid.appendChild(track);
|
|
567
|
-
var n = document.createElement('span'); n.className = 'mono font-bold shrink-0'; n.textContent = fmt(modelTotal(byModel, m));
|
|
568
|
-
li.appendChild(badge); li.appendChild(mid); li.appendChild(n);
|
|
569
|
-
ol.appendChild(li);
|
|
570
|
-
});
|
|
571
|
-
if (!tops.length) ol.innerHTML = '<li class="mono text-sm px-4 py-6" style="color: var(--dim)">no session tokens yet</li>';
|
|
572
|
-
}
|
|
573
|
-
|
|
574
|
-
function renderTools() {
|
|
575
|
-
var body = document.getElementById('top');
|
|
576
|
-
body.innerHTML = '';
|
|
577
|
-
var tools = DATA.byTool || [], max = 1, sum = 0;
|
|
578
|
-
tools.forEach(function (r) { max = Math.max(max, r[1]); sum += r[1]; });
|
|
579
|
-
tools.forEach(function (r, i) {
|
|
580
|
-
var tr = document.createElement('tr');
|
|
581
|
-
tr.className = 'tilt' + (i < tools.length - 1 ? ' rowline' : '');
|
|
582
|
-
var share = sum ? Math.round(r[1] / sum * 100) : 0;
|
|
583
|
-
tr.innerHTML = '<td class="text-right pr-3 py-2.5 mono text-xs w-10" style="color: var(--dim)"></td>' +
|
|
584
|
-
'<td class="py-2.5 pr-3 truncate" style="max-width: 280px"></td>' +
|
|
585
|
-
'<td class="text-right py-2.5 pr-3 font-bold"></td>' +
|
|
586
|
-
'<td class="text-right py-2.5 pr-3" style="color: var(--dim)"></td>' +
|
|
587
|
-
'<td class="py-2.5 min-w-32"><div class="bar-track h-1.5 overflow-hidden"><div class="bar-fill h-full"></div></div></td>';
|
|
588
|
-
var tds = tr.children;
|
|
589
|
-
tds[0].textContent = String(i + 1).padStart(2, '0');
|
|
590
|
-
tds[1].textContent = r[0];
|
|
591
|
-
tds[1].title = r[0];
|
|
592
|
-
tds[2].textContent = fmt(r[1]);
|
|
593
|
-
tds[3].textContent = share + '%';
|
|
594
|
-
tds[4].firstChild.firstChild.style.width = Math.max(2, Math.round(r[1] / max * 100)) + '%';
|
|
595
|
-
body.appendChild(tr);
|
|
596
|
-
});
|
|
597
|
-
document.getElementById('topTable').style.display = tools.length ? '' : 'none';
|
|
598
|
-
document.getElementById('emptyTop').classList.toggle('hidden', tools.length > 0);
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
function renderStrip(t) {
|
|
602
|
-
var defs = [
|
|
603
|
-
['input', t.input, 'arrow-down-to-line'],
|
|
604
|
-
['output', t.output, 'arrow-up-from-line'],
|
|
605
|
-
['cache read', t.cacheRead, 'hard-drive-download'],
|
|
606
|
-
['cache write', t.cacheWrite, 'hard-drive-upload']
|
|
607
|
-
].filter(function (b) { return b[1] > 0; });
|
|
608
|
-
if (!defs.length) defs = [['input', 0, 'arrow-down-to-line'], ['output', 0, 'arrow-up-from-line']];
|
|
609
|
-
var strip = document.getElementById('tokens');
|
|
610
|
-
strip.innerHTML = '';
|
|
611
|
-
defs.forEach(function (b, i) {
|
|
612
|
-
var cell = document.createElement('div');
|
|
613
|
-
cell.className = 'stripcell flex-1 px-4 py-4' + (i > 0 ? ' sm:border-l' : '');
|
|
614
|
-
cell.style.borderColor = 'var(--line)';
|
|
615
|
-
cell.innerHTML = '<div class="flex items-center gap-2 mono text-[11px] uppercase tracking-[0.14em]" style="color: var(--dim)">' +
|
|
616
|
-
'<i data-lucide="' + b[2] + '" class="size-3.5"></i><span></span></div>' +
|
|
617
|
-
'<p class="mono font-bold text-3xl mt-1"></p>';
|
|
618
|
-
cell.querySelector('span').textContent = b[0];
|
|
619
|
-
cell.querySelector('p').textContent = fmt(b[1]);
|
|
620
|
-
strip.appendChild(cell);
|
|
621
|
-
});
|
|
622
|
-
}
|
|
623
|
-
|
|
624
|
-
function money(v) { return '$' + v.toFixed(2); }
|
|
625
|
-
|
|
626
|
-
// 14-day volume sparkline + blended-rate daily stats for the USD card.
|
|
627
|
-
// Daily cost is a blended estimate (total USD / total tokens x day
|
|
628
|
-
// tokens), always rendered with ~ and never mixed with measured totals.
|
|
629
|
-
function renderSpark(byDay, usd, savedUsd, total) {
|
|
630
|
-
var el = document.getElementById('usdSpark');
|
|
631
|
-
el.innerHTML = '';
|
|
632
|
-
var keys = Object.keys(byDay).sort().slice(-14);
|
|
633
|
-
var rate = total ? usd / total : 0;
|
|
634
|
-
var max = 1, vols = [];
|
|
635
|
-
keys.forEach(function (k) {
|
|
636
|
-
var v = dayTotal(byDay[k] || {});
|
|
637
|
-
vols.push({ k: k, v: v });
|
|
638
|
-
max = Math.max(max, v);
|
|
639
|
-
});
|
|
640
|
-
var top = vols.slice().sort(function (a, b) { return b.v - a.v; })[0] || { k: '', v: 0 };
|
|
641
|
-
vols.forEach(function (r) {
|
|
642
|
-
var s = document.createElement('span');
|
|
643
|
-
if (r.k === top.k && r.v > 0) s.className = 'top';
|
|
644
|
-
s.style.height = Math.max(4, Math.round(r.v / max * 100)) + '%';
|
|
645
|
-
s.title = r.k + ': ' + fmt(r.v) + ' tokens (~$' + (rate * r.v).toFixed(2) + ')';
|
|
646
|
-
el.appendChild(s);
|
|
647
|
-
});
|
|
648
|
-
var days = keys.length || 1;
|
|
649
|
-
document.getElementById('usdPerDay').textContent = '$' + (usd / days).toFixed(2);
|
|
650
|
-
document.getElementById('usdTopDay').textContent = top.k ? top.k.slice(5) + ' ~$' + (rate * top.v).toFixed(2) : '-';
|
|
651
|
-
document.getElementById('usdLeverage').textContent = usd ? (savedUsd / usd).toFixed(1) : '-';
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
// Week-over-week sums from byDay: { cur, prev } for a token selector.
|
|
655
|
-
function weekSplit(days, pick) {
|
|
656
|
-
var keys = Object.keys(days).sort(), cur = 0, prev = 0;
|
|
657
|
-
var last = keys.slice(-14);
|
|
658
|
-
last.forEach(function (k, i) {
|
|
659
|
-
var v = pick(days[k] || {});
|
|
660
|
-
if (i < last.length - 7) prev += v; else cur += v;
|
|
661
|
-
});
|
|
662
|
-
return { cur: cur, prev: prev };
|
|
663
|
-
}
|
|
664
|
-
function pillHTML(cur, prev, goodWhenUp) {
|
|
665
|
-
if (!prev) return { text: cur ? 'new' : '—', cls: 'flat' };
|
|
666
|
-
var p = (cur - prev) / prev * 100, a = Math.abs(p);
|
|
667
|
-
var cls = a < 0.5 ? 'flat' : ((p > 0) === goodWhenUp ? 'good' : 'bad');
|
|
668
|
-
return { text: (p >= 0 ? '▲ +' : '▼ ') + a.toFixed(1) + '% vs prior 7d', cls: cls };
|
|
669
|
-
}
|
|
670
|
-
function setPill(id, cur, prev, goodWhenUp) {
|
|
671
|
-
var el = document.getElementById(id), r = pillHTML(cur, prev, goodWhenUp);
|
|
672
|
-
el.textContent = r.text;
|
|
673
|
-
el.className = 'pill mono ' + r.cls;
|
|
674
|
-
}
|
|
675
|
-
function dayTotal(b) { return (b.input || 0) + (b.output || 0) + (b.cacheRead || 0) + (b.cacheWrite || 0); }
|
|
676
|
-
|
|
677
|
-
function render(d) {
|
|
678
|
-
DATA = d;
|
|
679
|
-
var t = d.tokens || { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
|
|
680
|
-
var total = t.input + t.output + t.cacheRead + t.cacheWrite;
|
|
681
|
-
var sub = document.getElementById('herosub');
|
|
682
|
-
sub.innerHTML = '';
|
|
683
|
-
('input ' + fmt(t.input) + ' · output ' + fmt(t.output) + ' · cache read ' + fmt(t.cacheRead) +
|
|
684
|
-
(t.cacheWrite ? ' · cache write ' + fmt(t.cacheWrite) : '') + ' · ' + money(d.usd || 0)).split(' ').forEach(function (w) {
|
|
685
|
-
var s = document.createElement('span'); s.textContent = w + ' ';
|
|
686
|
-
sub.appendChild(s);
|
|
687
|
-
});
|
|
688
|
-
var totalEl = document.getElementById('total');
|
|
689
|
-
if (hasGsap) {
|
|
690
|
-
var o = { v: 0 };
|
|
691
|
-
gsap.to(o, { v: total, duration: 1.2, ease: 'power3.out', onUpdate: function () { totalEl.textContent = fmt(o.v); } });
|
|
692
|
-
} else {
|
|
693
|
-
totalEl.textContent = fmt(total);
|
|
694
|
-
}
|
|
695
|
-
countUp(document.getElementById('usd'), d.usd || 0, true);
|
|
696
|
-
document.getElementById('usdNote').textContent = d.priced ? 'per-model price table' : 'incl. default pricing';
|
|
697
|
-
var byDay = d.byDay || {};
|
|
698
|
-
renderSpark(byDay, d.usd || 0, d.savedUsd || 0, total);
|
|
699
|
-
document.getElementById('verChip').textContent = 'tersio v' + (d.version || '?');
|
|
700
|
-
var wAll = weekSplit(byDay, dayTotal);
|
|
701
|
-
setPill('usdDelta', wAll.cur, wAll.prev, false);
|
|
702
|
-
countUp(document.getElementById('saved'), d.savedUsd || 0, true);
|
|
703
|
-
var wCache = weekSplit(byDay, function (b) { return b.cacheRead || 0; });
|
|
704
|
-
setPill('savedDelta', wCache.cur, wCache.prev, true);
|
|
705
|
-
document.getElementById('co2').textContent = '~' + (d.co2g || 0).toFixed(1) + 'g';
|
|
706
|
-
var wOut = weekSplit(byDay, function (b) { return b.output || 0; });
|
|
707
|
-
setPill('co2Delta', wOut.cur, wOut.prev, false);
|
|
708
|
-
var share = total ? Math.round((t.cacheRead + t.cacheWrite) / total * 100) : 0;
|
|
709
|
-
document.getElementById('cacheShare').textContent = share + '%';
|
|
710
|
-
var shareOf = function (b) { var s = dayTotal(b); return s ? ((b.cacheRead || 0) + (b.cacheWrite || 0)) / s * 100 : 0; };
|
|
711
|
-
var keys = Object.keys(byDay).sort(), cPct = 0, pPct = 0, cn = 0, pn = 0;
|
|
712
|
-
keys.slice(-7).forEach(function (k) { cPct += shareOf(byDay[k]); cn++; });
|
|
713
|
-
keys.slice(-14, -7).forEach(function (k) { pPct += shareOf(byDay[k]); pn++; });
|
|
714
|
-
var pp = (cn ? cPct / cn : 0) - (pn ? pPct / pn : 0);
|
|
715
|
-
var cd = document.getElementById('cacheDelta');
|
|
716
|
-
cd.textContent = (pp >= 0 ? '▲ +' : '▼ ') + Math.abs(pp).toFixed(1) + 'pp vs prior 7d';
|
|
717
|
-
cd.className = 'pill mono ' + (Math.abs(pp) < 0.05 ? 'flat' : (pp > 0 ? 'good' : 'bad'));
|
|
718
|
-
|
|
719
|
-
renderStrip(t);
|
|
720
|
-
renderGraph(d.byDay || {});
|
|
721
|
-
renderBars();
|
|
722
|
-
renderModels();
|
|
723
|
-
renderTools();
|
|
724
|
-
|
|
725
|
-
if (window.lucide) lucide.createIcons();
|
|
726
|
-
if (hasGsap) {
|
|
727
|
-
gsap.from('.hero-in', { y: 26, opacity: 0, duration: 0.8, ease: 'power3.out', stagger: 0.08 });
|
|
728
|
-
// Scrub: hero sub words fade in sequence on scroll.
|
|
729
|
-
if (hasST) {
|
|
730
|
-
gsap.fromTo('#herosub span', { opacity: 0.15 }, {
|
|
731
|
-
opacity: 1, ease: 'none', stagger: 0.05,
|
|
732
|
-
scrollTrigger: { trigger: '#herosub', start: 'top 92%', end: 'top 55%', scrub: true }
|
|
733
|
-
});
|
|
734
|
-
}
|
|
735
|
-
}
|
|
736
|
-
observe();
|
|
737
|
-
}
|
|
738
|
-
|
|
739
|
-
var seen = new WeakSet();
|
|
740
|
-
function observe() {
|
|
741
|
-
var els = document.querySelectorAll('.rise');
|
|
742
|
-
if (!('IntersectionObserver' in window) || reduce) {
|
|
743
|
-
els.forEach(function (el) { el.classList.add('in'); });
|
|
744
|
-
return;
|
|
745
|
-
}
|
|
746
|
-
var io = new IntersectionObserver(function (entries) {
|
|
747
|
-
entries.forEach(function (e) {
|
|
748
|
-
if (e.isIntersecting && !seen.has(e.target)) {
|
|
749
|
-
seen.add(e.target); e.target.classList.add('in'); io.unobserve(e.target);
|
|
750
|
-
}
|
|
751
|
-
});
|
|
752
|
-
}, { threshold: 0.12 });
|
|
753
|
-
els.forEach(function (el) { if (!seen.has(el)) io.observe(el); });
|
|
754
|
-
}
|
|
755
|
-
|
|
756
|
-
document.querySelectorAll('.seg button').forEach(function (btn) {
|
|
757
|
-
btn.addEventListener('click', function () {
|
|
758
|
-
document.querySelectorAll('.seg button').forEach(function (b) { b.classList.remove('on'); });
|
|
759
|
-
btn.classList.add('on');
|
|
760
|
-
RANGE = btn.getAttribute('data-range');
|
|
761
|
-
if (DATA) renderBars();
|
|
762
|
-
});
|
|
763
|
-
});
|
|
764
|
-
|
|
765
|
-
function load() {
|
|
766
|
-
fetch('data.json').then(function (r) { return r.json(); }).then(render).catch(function () {
|
|
767
|
-
document.getElementById('rawlink').style.display = 'none';
|
|
768
|
-
document.getElementById('topTable').style.display = 'none';
|
|
769
|
-
document.getElementById('emptyTop').classList.remove('hidden');
|
|
770
|
-
if (window.lucide) lucide.createIcons();
|
|
771
|
-
observe();
|
|
772
|
-
});
|
|
773
|
-
}
|
|
774
|
-
document.getElementById('reload').addEventListener('click', load);
|
|
775
|
-
load();
|
|
776
|
-
observe();
|
|
777
|
-
})();
|
|
778
|
-
</script>
|
|
779
|
-
</body>
|
|
780
|
-
</html>
|