-
Notifications
You must be signed in to change notification settings - Fork 15
Expand file tree
/
Copy pathperformance.html
More file actions
341 lines (341 loc) · 42.1 KB
/
Copy pathperformance.html
File metadata and controls
341 lines (341 loc) · 42.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
<!doctype html>
<html lang=en>
<head>
<meta charset=utf-8>
<meta name=viewport content="width=device-width,initial-scale=1">
<title>Token Meter — Performance card</title>
<link rel=icon type="image/svg+xml" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' rx='14' fill='%2307090c'/%3E%3Cpath fill='%23fff' d='M47.03 36.16v-7.58L10 10v8.32l28.7 13.97L10 46.44v8.14l37.03-18.4z'/%3E%3C/svg%3E">
<meta name=theme-color content="#07090c">
<style>
@font-face{font-family:"Tektur Local";src:url("/assets/fonts/Tektur-Variable.ttf") format("truetype");font-style:normal;font-weight:400 900;font-stretch:75% 100%;font-display:swap}
:root{color-scheme:dark;--bg:#07090c;--panel:#111820;--panel2:#17212b;--line:rgba(171,223,238,.09);--line2:rgba(177,228,241,.17);--edge:rgba(127,219,242,.38);--fg:#f6f8fb;--dim:#a8b3c1;--faint:#7d8ba0;--cyan:#00bceb;--blue:#1ba0e1;--sky:#7fdbf2;--violet:#c7a7ff;--warn:#ffb457;--paper:#f1eee4;--ink:#10191d;--active:linear-gradient(132deg,var(--cyan),var(--blue) 48%,var(--sky));--card:radial-gradient(ellipse 82% 90% at 102% -8%,rgba(127,219,242,.115),transparent 58%),radial-gradient(ellipse 74% 74% at -8% 108%,rgba(199,167,255,.075),transparent 62%),linear-gradient(145deg,rgba(22,32,42,.99),rgba(12,19,26,.99) 55%,rgba(8,13,19,.995));--shadow:0 1px 0 rgba(215,246,253,.16),0 20px 46px rgba(0,0,0,.36),0 8px 24px rgba(0,125,168,.1)}
*{box-sizing:border-box}
html,body{margin:0;min-height:100%;background:var(--bg);color:var(--fg);font:14px/1.5 -apple-system,BlinkMacSystemFont,"Segoe UI",Inter,system-ui,sans-serif;-webkit-font-smoothing:antialiased}
body{background:radial-gradient(ellipse 72% 48% at 13% -9%,rgba(0,188,235,.21),transparent 62%),radial-gradient(ellipse 56% 42% at 96% 2%,rgba(199,167,255,.14),transparent 60%),radial-gradient(ellipse 56% 38% at 68% 105%,rgba(27,160,225,.1),transparent 68%),linear-gradient(155deg,#05070a 0%,#09131b 42%,#0c111a 68%,#07090c 100%)}
button,input,select{font:inherit}button,a,input,select{outline:none}button{cursor:pointer}
button:focus-visible,a:focus-visible,input:focus-visible,select:focus-visible{outline:2px solid var(--cyan);outline-offset:2px}
.performanceShell{--rail-width:184px;display:grid;grid-template-columns:var(--rail-width) minmax(0,1320px);column-gap:20px;align-items:start;width:100%;max-width:1532px;min-height:100vh;margin:0 auto;padding:18px 24px 48px}
.performanceRail{position:sticky;top:18px;display:flex;height:calc(100vh - 36px);min-height:520px;flex-direction:column;gap:12px;padding:12px;border:1px solid var(--edge);border-radius:10px;background:radial-gradient(ellipse 120% 38% at 18% -4%,rgba(0,188,235,.22),transparent 62%),radial-gradient(ellipse 110% 42% at 118% 84%,rgba(199,167,255,.11),transparent 62%),linear-gradient(165deg,rgba(15,27,36,.995),rgba(8,15,22,.995) 52%,rgba(7,11,17,.998));box-shadow:var(--shadow)}
.performanceRail:before{content:"";position:absolute;inset:0 12px auto;height:1px;background:linear-gradient(90deg,transparent,var(--cyan) 24%,var(--sky) 58%,rgba(199,167,255,.54) 80%,transparent);opacity:.65}
.railBrand{display:flex;min-width:0;align-items:center;gap:9px;padding:3px 2px 10px;color:var(--fg);font-weight:780;text-decoration:none;white-space:nowrap}.railBrand img{display:block;width:50px;height:auto;max-height:30px;object-fit:contain}.railBrandName{min-width:0;overflow:hidden;text-overflow:ellipsis}
.railNav{display:flex;min-height:0;flex:1;flex-direction:column;gap:4px;padding-top:10px;border-top:1px solid var(--line)}.railPrimary,.railSecondary{display:flex;flex-direction:column;gap:4px}.railSecondary{margin-top:auto;padding-top:10px;border-top:1px solid var(--line)}
.railLink{display:grid;width:100%;min-height:42px;grid-template-columns:18px minmax(0,1fr);align-items:center;gap:10px;padding:9px 10px;border-radius:6px;color:var(--dim);font-size:13px;font-weight:700;text-decoration:none}.railLink:hover{background:linear-gradient(135deg,rgba(0,188,235,.105),rgba(199,167,255,.055));color:var(--fg)}.railLink.on{background:var(--active);color:#062430;box-shadow:0 8px 22px rgba(0,156,201,.22),inset 0 1px 0 rgba(255,255,255,.38)}
.railIcon{width:18px;height:18px;fill:none;stroke:currentColor;stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round}.railLabel{min-width:0;overflow:hidden;text-overflow:ellipsis}
.performanceSurface{grid-column:2;min-width:0}
.performanceHeader{position:relative;isolation:isolate;display:flex;height:138px;align-items:flex-start;overflow:hidden;margin-bottom:14px;padding:24px 30px;border:1px solid var(--edge);border-radius:8px;background:linear-gradient(116deg,rgba(13,28,37,.995),rgba(7,15,22,.995) 52%,rgba(9,13,24,.99));box-shadow:var(--shadow)}
.performanceHeader:after{content:"";position:absolute;right:0;bottom:0;left:0;height:1px;background:linear-gradient(90deg,transparent 2%,rgba(0,188,235,.76) 24%,rgba(127,219,242,.7) 56%,rgba(199,167,255,.62) 79%,transparent 98%)}
.headerCopy{position:relative;z-index:3}.headerEyebrow{display:block;margin-bottom:4px;color:var(--sky);font:800 10px/1 ui-monospace,"SF Mono",Menlo,monospace;letter-spacing:.16em;text-transform:uppercase}.headerCopy h1{margin:0;font-family:"Tektur Local","Avenir Next Condensed",-apple-system,sans-serif;font-size:52px;font-stretch:88%;font-weight:610;line-height:.98;letter-spacing:-.035em}
.headerSignal{position:absolute;z-index:1;right:30px;bottom:0;display:flex;width:min(56%,650px);height:100%;align-items:flex-end;gap:6px;padding:18px 0 0;opacity:.72;transform:skewX(-8deg);mask-image:linear-gradient(90deg,transparent,#000 20%,#000 100%)}.headerSignal i{display:block;flex:1;height:var(--h);min-width:3px;background:linear-gradient(180deg,rgba(127,219,242,.08),rgba(0,188,235,.7));border-top:1px solid rgba(186,235,248,.65)}.headerSignal i:nth-child(3n){background:linear-gradient(180deg,rgba(199,167,255,.04),rgba(199,167,255,.46))}.headerSignal i:nth-child(5n){background:linear-gradient(180deg,rgba(255,180,87,.03),rgba(255,180,87,.28))}
.studioWorkspace{display:grid;grid-template-columns:220px minmax(0,1fr);gap:14px;align-items:start}
.controlDock{position:sticky;top:18px;display:flex;flex-direction:column;gap:18px;align-items:stretch;margin:0;padding:16px;border:1px solid var(--line2);border-radius:8px;background:var(--card);box-shadow:0 1px 0 rgba(255,255,255,.055),0 12px 30px rgba(0,0,0,.24)}
.field{display:grid;min-width:0;gap:6px}.field>span,.field legend{color:var(--faint);font-size:9px;font-weight:850;letter-spacing:.13em;text-transform:uppercase}.field input,.field select{width:100%;height:40px;min-width:0;border:1px solid var(--line2);border-radius:6px;background:radial-gradient(circle at 88% 0,rgba(127,219,242,.08),transparent 46%),linear-gradient(145deg,rgba(22,34,44,.96),rgba(9,16,23,.98));box-shadow:inset 0 1px 0 rgba(255,255,255,.035),0 5px 14px rgba(0,0,0,.1);color:var(--fg);padding:0 11px}.field input::placeholder{color:#647483}.field input:hover,.field select:hover{border-color:var(--edge)}
fieldset.field{border:0;padding:0;margin:0}.segments{display:grid;grid-template-columns:repeat(3,1fr);gap:3px;padding:3px;border:1px solid var(--line2);border-radius:6px;background:#071015}.segments button{height:32px;border:0;border-radius:4px;background:transparent;color:var(--dim);font-size:11px;font-weight:800}.segments button:hover{color:var(--fg);background:rgba(0,188,235,.08)}.segments button.on{background:var(--active);color:#051116;box-shadow:0 5px 14px rgba(0,156,201,.2)}
.toggleGroup{display:grid;gap:4px;padding-bottom:1px}.toggle{display:flex;min-height:20px;align-items:center;justify-content:space-between;gap:8px;color:var(--dim);font-size:10px;font-weight:700}.toggle input{position:relative;width:29px;height:16px;flex:0 0 auto;margin:0;appearance:none;border:1px solid var(--line2);border-radius:999px;background:#071015}.toggle input:after{content:"";position:absolute;top:2px;left:2px;width:10px;height:10px;border-radius:50%;background:var(--faint);transition:left .15s,background .15s}.toggle input:checked{border-color:rgba(0,188,235,.52);background:rgba(0,188,235,.17)}.toggle input:checked:after{left:15px;background:var(--sky)}.toggle input:disabled{opacity:.38;cursor:not-allowed}
.stateGroup{display:grid;min-width:0;padding-top:2px;border-top:1px solid var(--line)}#status{min-height:15px;margin:10px 0 0;color:var(--faint);font-size:10px;overflow-wrap:anywhere}#status[data-kind=error]{color:#ffc078}#status[data-kind=success]{color:#7fe0bc}
.actions{display:grid;grid-template-columns:1fr;gap:7px}.actions button{width:100%;height:40px;border:1px solid var(--line2);border-radius:6px;background:#0a141b;color:var(--dim);padding:0 13px;font-weight:760;white-space:nowrap}.actions button:hover:not(:disabled){border-color:var(--edge);color:var(--fg)}.actions #download{border:0;background:var(--active);color:#06141a;font-weight:900;padding:0 12px;box-shadow:0 8px 20px rgba(0,156,201,.2)}.actions button:disabled{opacity:.42;cursor:not-allowed}
.stage{position:relative;display:grid;place-items:center;min-height:720px;overflow:hidden;padding:62px 42px 48px;border:1px solid var(--line2);border-radius:8px;background:radial-gradient(ellipse 44% 40% at 50% 46%,rgba(0,188,235,.09),transparent 70%),radial-gradient(ellipse 55% 55% at 83% 14%,rgba(199,167,255,.07),transparent 72%),linear-gradient(rgba(127,219,242,.025) 1px,transparent 1px),linear-gradient(90deg,rgba(127,219,242,.025) 1px,transparent 1px),linear-gradient(145deg,rgba(13,24,32,.98),rgba(6,12,18,.99));background-size:auto,auto,28px 28px,28px 28px,auto;box-shadow:var(--shadow)}
.stageMeta{position:absolute;z-index:3;top:18px;right:22px;left:22px;display:flex;justify-content:space-between;color:var(--faint);font:800 9px/1 ui-monospace,"SF Mono",Menlo,monospace;letter-spacing:.14em;text-transform:uppercase}
.posterFrame{position:relative;width:min(100%,620px)}.posterFrame:before,.posterFrame:after{content:"";position:absolute;pointer-events:none}.posterFrame:before{z-index:0;inset:-13px 14px 13px -14px;border:1px solid rgba(0,188,235,.32);background:rgba(0,188,235,.03)}.posterFrame:after{z-index:0;inset:13px -14px -13px 14px;border:1px solid rgba(199,167,255,.22);background:rgba(199,167,255,.025)}
#poster{position:relative;z-index:1;display:block;width:100%;height:auto;aspect-ratio:4/5;background:var(--paper);box-shadow:0 1px 0 rgba(255,255,255,.24),0 38px 100px rgba(0,0,0,.58),0 0 42px rgba(0,188,235,.06)}
[hidden]{display:none!important}
@media(max-width:1180px){.performanceShell{--rail-width:68px;column-gap:16px;padding-right:18px;padding-left:18px}.performanceRail{padding:10px 8px}.railBrand{justify-content:center;padding-right:0;padding-left:0}.railBrand img{width:50px}.railBrandName,.railLabel{display:none}.railLink{grid-template-columns:1fr;place-items:center;padding:9px}.studioWorkspace{grid-template-columns:190px minmax(0,1fr)}}
@media(max-width:1024px){.performanceShell{column-gap:14px;padding:14px}.performanceRail{top:14px;height:calc(100vh - 28px)}.performanceHeader{height:126px;padding:22px 24px}.headerCopy h1{font-size:46px}.headerSignal{right:24px;width:61%}.studioWorkspace{grid-template-columns:180px minmax(0,1fr);gap:12px}.controlDock{top:14px;padding:13px;gap:15px}.stage{min-height:680px;padding:58px 28px 42px}.posterFrame{width:min(100%,560px)}}
@media(max-width:760px){.performanceShell{display:block;padding:12px 14px 40px}.performanceRail{position:sticky;top:0;z-index:10;display:grid;height:auto;min-height:0;grid-template-columns:auto minmax(0,1fr);align-items:center;margin-bottom:14px;padding:8px 0 10px;border:0;border-bottom:1px solid var(--edge);border-radius:0}.railBrand{justify-content:flex-start;padding:0}.railBrandName{display:inline}.railNav{grid-column:1/-1;overflow-x:auto;flex-direction:row;padding-top:8px}.railPrimary,.railSecondary{display:contents}.railLink{display:flex;width:auto;min-height:38px;gap:7px;padding:8px 11px;white-space:nowrap}.railLabel{display:inline}.performanceSurface{width:100%}.performanceHeader{height:112px}.headerSignal{right:20px;width:72%}.studioWorkspace{display:block}.controlDock{position:static;display:grid;grid-template-columns:repeat(2,minmax(0,1fr));margin-bottom:14px}.controlDock>*{grid-column:span 1}.actions{grid-template-columns:repeat(2,minmax(0,1fr))}.stage{padding:56px 24px 36px}}
</style>
</head>
<body class="spectrumApp performanceApp">
<div class=performanceShell>
<aside class=performanceRail id=performance-rail aria-label="Token Meter navigation">
<a class=railBrand href="/#sessions" aria-label="Token Meter dashboard"><img src="/assets/brand/logo-splunk-acc-rgb-w.png" alt=""><span class=railBrandName>Token Meter</span></a>
<nav class=railNav id=performance-navigation aria-label="Primary navigation">
<div class=railPrimary>
<a class=railLink href="/#sessions" aria-label=Sessions title=Sessions><svg class=railIcon viewBox="0 0 24 24" aria-hidden=true><path d="M4 7h16M4 12h16M4 17h16"/><path d="M7 5v4M14 10v4M18 15v4"/></svg><span class=railLabel>Sessions</span></a>
<a class=railLink href="/#spend" aria-label=Spend title=Spend><svg class=railIcon viewBox="0 0 24 24" aria-hidden=true><rect x="4" y="5" width="16" height="15" rx="2"/><path d="M8 3v4M16 3v4M4 9h16M8 13h3M8 17h7"/></svg><span class=railLabel>Spend</span></a>
<a class=railLink href="/#models" aria-label=Models title=Models><svg class=railIcon viewBox="0 0 24 24" aria-hidden=true><path d="M4 18l5-5 4 2 7-8"/><path d="M16 7h4v4M4 20h16"/></svg><span class=railLabel>Models</span></a>
<a class=railLink href="/#efficiency" aria-label=Efficiency title=Efficiency><svg class=railIcon viewBox="0 0 24 24" aria-hidden=true><path d="M5 19V9M12 19V5M19 19v-7"/><path d="M3 19h18"/></svg><span class=railLabel>Efficiency</span></a>
<a class=railLink href="/#git" aria-label=Git title=Git><svg class=railIcon viewBox="0 0 24 24" aria-hidden=true><path d="M4 16l5-5 4 3 7-8"/><path d="M15 6h5v5M4 20h16"/></svg><span class=railLabel>Git</span></a>
<a class="railLink on" id=tab-performance href=/performance aria-label=Performance aria-current=page title=Performance><svg class=railIcon viewBox="0 0 24 24" aria-hidden=true><rect x="5" y="4" width="14" height="16" rx="2"/><path d="M8 15v-3M12 15V9M16 15v-5M8 18h8"/></svg><span class=railLabel>Performance</span></a>
</div>
<div class=railSecondary>
<a class=railLink href="/#learn" aria-label=Learn title=Learn><svg class=railIcon viewBox="0 0 24 24" aria-hidden=true><path d="M4 5.5A3.5 3.5 0 0 1 7.5 2H11v17H7.5A3.5 3.5 0 0 0 4 22zM20 5.5A3.5 3.5 0 0 0 16.5 2H13v17h3.5A3.5 3.5 0 0 1 20 22z"/></svg><span class=railLabel>Learn</span></a>
<a class=railLink href="/#capabilities" aria-label=Tools title=Tools><svg class=railIcon viewBox="0 0 24 24" aria-hidden=true><path d="M14 6a4 4 0 0 0-5 5L4 16l4 4 5-5a4 4 0 0 0 5-5l-3 3-3-3z"/></svg><span class=railLabel>Tools</span></a>
<a class=railLink href="/#settings" aria-label=Settings title=Settings><svg class=railIcon viewBox="0 0 24 24" aria-hidden=true><path d="M4 7h10M18 7h2M4 17h3M11 17h9M14 4v6M7 14v6"/></svg><span class=railLabel>Settings</span></a>
</div>
</nav>
</aside>
<main class=performanceSurface id=performance-surface>
<header class=performanceHeader id=performance-header>
<div class=headerCopy><span class=headerEyebrow>Builder field sheet</span><h1>Performance card</h1></div>
<div class=headerSignal aria-hidden=true><i style="--h:26%"></i><i style="--h:44%"></i><i style="--h:34%"></i><i style="--h:68%"></i><i style="--h:52%"></i><i style="--h:81%"></i><i style="--h:43%"></i><i style="--h:72%"></i><i style="--h:57%"></i><i style="--h:91%"></i><i style="--h:63%"></i><i style="--h:76%"></i><i style="--h:48%"></i><i style="--h:84%"></i><i style="--h:59%"></i><i style="--h:70%"></i></div>
</header>
<div class=studioWorkspace>
<section class=controlDock id=performance-controls aria-label="Performance controls">
<fieldset class=field id=range-controls><legend>Period</legend><div class=segments><button type=button data-range=7>7D</button><button class=on type=button data-range=30>30D</button><button type=button data-range=90>90D</button></div></fieldset>
<label class=field><span>Name</span><input id=builder-name maxlength=40 autocomplete=off placeholder="Optional"></label>
<div class=toggleGroup>
<label class=toggle><span>Usage charts</span><input id=include-usage type=checkbox checked></label>
</div>
<div class=stateGroup><p id=status role=status aria-live=polite>Loading local recap evidence…</p></div>
<div class=actions><button id=reset type=button>Reset</button><button id=retry type=button hidden>Retry</button><button id=download type=button disabled>Download PNG</button></div>
</section>
<section class=stage id=performance-preview aria-label="Builder Field Sheet preview">
<div class=stageMeta aria-hidden=true><span>Live preview</span><span>1080 × 1350 · PNG</span></div>
<div class=posterFrame><img id=performance-card-logo src="/assets/brand/logo-splunk-acc-rgb-w.png" alt="" hidden><canvas id=poster width=1080 height=1350 role=img aria-label="Builder recap preview loading"></canvas></div>
</section>
</div>
</main>
</div>
<script>
const $=id=>document.getElementById(id);
const builderRecapState={range:30,name:'',includeUsage:true,payload:null,request:0,controller:null,loading:false,error:'',rendered:false,logoError:false};
function safeArray(value){return Array.isArray(value)?value:[];}
function sanitizeBuilderName(value){return String(value??'').replace(/[\u0000-\u001f\u007f]+/g,' ').replace(/\s+/g,' ').trim().slice(0,40);}
function compactNumber(value){
const number=Number(value);
if(!Number.isFinite(number))return '—';
const absolute=Math.abs(number);
if(absolute>=1e6)return `${(number/1e6).toFixed(absolute>=1e7?1:2).replace(/\.0+$|(?<=\.[0-9])0$/,'')}M`;
if(absolute>=1e3)return `${(number/1e3).toFixed(absolute>=1e4?1:2).replace(/\.0+$|(?<=\.[0-9])0$/,'')}K`;
if(Number.isInteger(number))return String(number);
return number.toFixed(1).replace(/\.0$/,'');
}
function durationLabel(value){
const seconds=Math.max(0,Math.round(Number(value)||0)),hours=Math.floor(seconds/3600),minutes=Math.floor(seconds%3600/60);
if(hours)return `${hours}H ${String(minutes).padStart(2,'0')}M`;
if(minutes)return `${minutes}M`;
return `${seconds}S`;
}
function formatStat(stat){
if(!stat||stat.available!==true||!Number.isFinite(Number(stat.current)))return '—';
if(stat.id==='lines_pushed'||stat.id==='commits_pushed')return Math.round(Number(stat.current)).toLocaleString('en-US');
const unit=String(stat.unit||'').toLowerCase();
if(unit==='seconds')return durationLabel(stat.current);
return compactNumber(stat.current);
}
function formatSpend(stat){
if(!stat||stat.available!==true)return '—';
const amount=Number(stat.current);
if(!Number.isFinite(amount)||amount<0)return '—';
if(amount>=1000)return `$${compactNumber(amount)}`;
if(amount>=100)return `$${Math.round(amount)}`;
return `$${amount.toFixed(2)}`;
}
function ellipsize(ctx,value,maxWidth){
const text=String(value||'');
if(ctx.measureText(text).width<=maxWidth)return text;
let result=text;
while(result.length&&ctx.measureText(`${result}…`).width>maxWidth)result=result.slice(0,-1);
return `${result}…`;
}
function fitText(ctx,value,maxWidth,start=120,min=34){
let size=start;
while(size>min){ctx.font=`900 ${size}px -apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif`;if(ctx.measureText(String(value)).width<=maxWidth)break;size-=2;}
return size;
}
function supportingStats(payload,options){
options=options||{};
const raw=[...safeArray(payload&&payload.spotlights),...safeArray(payload&&payload.supporting)].filter(stat=>stat&&typeof stat==='object');
const efficiency=raw.find(stat=>stat.id==='efficiency_change')||{id:'efficiency_change',label:'Output efficiency',family:'efficiency',available:false,current:null,previous:null,delta_pct:null,unit:'output tokens/$'};
const speed=raw.find(stat=>stat.id==='speed_champion')||{id:'speed_champion',label:'Fastest model',family:'efficiency',available:false,current:null,previous:null,delta_pct:null,unit:'output tokens/s',leaders:[]};
const sessions=raw.find(stat=>stat.id==='sessions')||{id:'sessions',label:'AI Sessions',family:'activity',available:false,current:null,previous:null,delta_pct:null,unit:'sessions'};
return [efficiency,speed,sessions];
}
function gradientFill(ctx,x0,y0,x1,y1,stops){
if(typeof ctx.createLinearGradient!=='function')return stops[stops.length-1][1];
const gradient=ctx.createLinearGradient(x0,y0,x1,y1);stops.forEach(stop=>gradient.addColorStop(stop[0],stop[1]));return gradient;
}
function radialFill(ctx,x,y,radius,stops){
if(typeof ctx.createRadialGradient!=='function')return stops[stops.length-1][1];
const gradient=ctx.createRadialGradient(x,y,0,x,y,radius);stops.forEach(stop=>gradient.addColorStop(stop[0],stop[1]));return gradient;
}
function fillRoundedRect(ctx,x,y,width,height,radius){
if(typeof ctx.roundRect!=='function'||typeof ctx.beginPath!=='function'||typeof ctx.fill!=='function'){ctx.fillRect(x,y,width,height);return false;}
ctx.beginPath();ctx.roundRect(x,y,width,height,Math.min(radius,height/2,width/2));ctx.fill();return true;
}
function setTracking(ctx,value){if('letterSpacing' in ctx)ctx.letterSpacing=value;}
// Ordered Bayer dithering trades the painted gradients down to a coarse palette
// so the transitions carry a visible halftone texture. Keep `levels` high enough
// that the per-pixel error stays small, or the dark band turns to color speckle.
function ditherRegion(ctx,x,y,width,height,levels){
if(typeof ctx.getImageData!=='function'||typeof ctx.putImageData!=='function')return false;
const matrix=[0,32,8,40,2,34,10,42,48,16,56,24,50,18,58,26,12,44,4,36,14,46,6,38,60,28,52,20,62,30,54,22,3,35,11,43,1,33,9,41,51,19,59,27,49,17,57,25,15,47,7,39,13,45,5,37,63,31,55,23,61,29,53,21];
try{
const image=ctx.getImageData(x,y,width,height),data=image.data,rows=image.height,columns=image.width;
const steps=Math.max(2,Math.round(Number(levels)||6)),step=255/(steps-1);
for(let row=0;row<rows;row++){
const rowOffset=(row&7)*8;
for(let column=0;column<columns;column++){
const index=(row*columns+column)*4,bias=(matrix[rowOffset+(column&7)]/64-.5)*step;
for(let channel=0;channel<3;channel++)data[index+channel]=Math.min(255,Math.max(0,Math.round((data[index+channel]+bias)/step)*step));
}
}
ctx.putImageData(image,x,y);
}catch(error){return false;}
return true;
}
function drawUsageGroup(ctx,title,rows,x,y,width,color,accent){
const ranked=safeArray(rows).slice(0,3);if(!ranked.length)return;
const leader=ranked[0],leaderShare=Math.max(0,Math.min(100,Number(leader&&leader.share)||0));
ctx.fillStyle='#5c6a6f';setTracking(ctx,'1.4px');ctx.font='900 13px -apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif';ctx.fillText(title,x,y);setTracking(ctx,'0px');
ctx.fillStyle='#07171f';ctx.font='900 34px "Arial Narrow","Avenir Next Condensed",sans-serif';ctx.fillText(ellipsize(ctx,String(leader&&leader.label||'').toUpperCase(),width-104),x,y+48);
ctx.fillStyle=gradientFill(ctx,x+width-90,y+16,x+width,y+48,[[0,'#07171f'],[1,'#1d4a5c']]);
ctx.font='900 38px "Arial Narrow","Avenir Next Condensed",sans-serif';ctx.textAlign='right';ctx.fillText(`${Math.round(leaderShare)}%`,x+width,y+48);ctx.textAlign='left';
ctx.fillStyle='rgba(7,23,31,.11)';fillRoundedRect(ctx,x,y+68,width,12,6);
ctx.fillStyle=gradientFill(ctx,x,0,x+width,0,[[0,color],[1,accent||color]]);fillRoundedRect(ctx,x,y+68,Math.max(14,width*leaderShare/100),12,6);
ranked.slice(1).forEach((row,index)=>{
const top=y+118+index*46,share=Math.max(0,Math.min(100,Number(row&&row.share)||0));
ctx.fillStyle='#07171f';ctx.font='900 13px -apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif';ctx.fillText(ellipsize(ctx,String(row&&row.label||'').toUpperCase(),width-58),x,top);
ctx.fillStyle='#5c6a6f';ctx.textAlign='right';ctx.fillText(`${Math.round(share)}%`,x+width,top);ctx.textAlign='left';
ctx.fillStyle='rgba(7,23,31,.09)';fillRoundedRect(ctx,x,top+10,width,6,3);
ctx.fillStyle=gradientFill(ctx,x,0,x+width,0,index===0?[[0,'#0d2b38'],[1,'rgba(13,43,56,.55)']]:[[0,accent||color],[1,'rgba(64,84,214,.45)']]);
fillRoundedRect(ctx,x,top+10,Math.max(10,width*share/100),6,3);
});
}
function drawBuilderRecap(canvas,payload,options){
options=options||{};
canvas.width=1080;canvas.height=1350;
const ctx=canvas.getContext('2d',{willReadFrequently:true}),ink='#07171f',muted='#5c6a6f',cyan='#00bceb',cobalt='#4054d6',light='#f8f5ec',sky='#8ee9f2',violet='#8f6ce6';
ctx.fillStyle=gradientFill(ctx,0,0,760,720,[[0,'#0d2c3d'],[.44,'#081a27'],[1,'#040d14']]);ctx.fillRect(0,0,1080,720);
ctx.fillStyle=radialFill(ctx,960,-70,780,[[0,'rgba(0,188,235,.46)'],[.5,'rgba(0,188,235,.12)'],[1,'rgba(0,188,235,0)']]);ctx.fillRect(0,0,1080,720);
ctx.fillStyle=radialFill(ctx,140,700,640,[[0,'rgba(96,84,224,.34)'],[.55,'rgba(96,84,224,.08)'],[1,'rgba(96,84,224,0)']]);ctx.fillRect(0,0,1080,720);
ditherRegion(ctx,0,0,1080,720,14);
ctx.fillStyle=gradientFill(ctx,0,720,1080,1220,[[0,'#f8f5ee'],[.48,'#f1eee4'],[1,'#e4e0d3']]);ctx.fillRect(0,720,1080,500);
ctx.fillStyle=radialFill(ctx,900,752,560,[[0,'rgba(0,188,235,.18)'],[.55,'rgba(0,188,235,.05)'],[1,'rgba(0,188,235,0)']]);ctx.fillRect(0,720,1080,500);
ditherRegion(ctx,0,720,1080,500,16);
ctx.fillStyle=gradientFill(ctx,0,1220,1080,1350,[[0,'#0c2434'],[.6,'#07171f'],[1,'#050e15']]);ctx.fillRect(0,1220,1080,130);
ditherRegion(ctx,0,1220,1080,130,14);
ctx.fillStyle=gradientFill(ctx,0,0,0,1350,[[0,sky],[.26,cyan],[.66,cobalt],[1,violet]]);ctx.fillRect(0,0,16,1350);
ctx.fillStyle=gradientFill(ctx,0,0,0,720,[[0,'rgba(64,84,214,.92)'],[1,'rgba(64,84,214,.2)']]);ctx.fillRect(16,0,8,720);
const name=sanitizeBuilderName(options.name),range=[7,30,90].includes(Number(payload&&payload.range_days))?Number(payload.range_days):30;
const lineStat=safeArray(payload&&payload.spotlights).find(stat=>stat&&stat.id==='lines_pushed');
const lead=lineStat||{id:'lines_pushed',label:'Lines pushed',family:'delivery',available:false,current:null,previous:null,delta_pct:null,unit:'lines'};
const commitStat=safeArray(payload&&payload.spotlights).find(stat=>stat&&stat.id==='commits_pushed')||{id:'commits_pushed',label:'Commits pushed',family:'delivery',available:false,current:null,previous:null,delta_pct:null,unit:'commits'};
const logo=options.logo,hasLogo=Boolean(logo&&logo.complete&&Number(logo.naturalWidth)>0);if(hasLogo)ctx.drawImage(logo,72,44,148,59);
const brandX=hasLogo?258:72;
if(hasLogo){ctx.fillStyle=gradientFill(ctx,0,46,0,102,[[0,'rgba(127,219,242,.08)'],[.5,'rgba(127,219,242,.5)'],[1,'rgba(127,219,242,.08)']]);ctx.fillRect(236,46,2,56);}
ctx.fillStyle=gradientFill(ctx,brandX,60,brandX+330,96,[[0,light],[1,sky]]);setTracking(ctx,'1.5px');ctx.font='900 42px -apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif';ctx.fillText('TOKEN METER',brandX,88);
ctx.fillStyle=cyan;ctx.font='900 12px ui-monospace,"SF Mono",Menlo,monospace';setTracking(ctx,'3.4px');ctx.fillText('PERFORMANCE CARD',brandX,110);setTracking(ctx,'0px');
const pillLabel=`${range}D`;ctx.font=`900 28px "Arial Narrow","Avenir Next Condensed",sans-serif`;const pillWidth=Math.max(84,ctx.measureText(pillLabel).width+46),pillX=1008-pillWidth;
ctx.fillStyle=gradientFill(ctx,pillX,54,pillX+pillWidth,98,[[0,sky],[.55,cyan],[1,cobalt]]);fillRoundedRect(ctx,pillX,54,pillWidth,44,22);
ctx.fillStyle='#04121a';ctx.textAlign='center';ctx.fillText(pillLabel,pillX+pillWidth/2,85);ctx.textAlign='left';
ctx.fillStyle=gradientFill(ctx,0,0,1080,0,[[0,'rgba(0,188,235,.85)'],[.42,'rgba(127,219,242,.4)'],[1,'rgba(143,108,230,0)']]);ctx.fillRect(0,150,1080,1);
const endDay=String(payload&&payload.current&&payload.current.end_day||''),monthIndex=Number(endDay.slice(5,7))-1,year=endDay.slice(0,4),month=['JANUARY','FEBRUARY','MARCH','APRIL','MAY','JUNE','JULY','AUGUST','SEPTEMBER','OCTOBER','NOVEMBER','DECEMBER'][monthIndex]||'';
setTracking(ctx,'1.6px');ctx.font='800 14px -apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif';ctx.fillStyle='#a8bcc6';ctx.fillText(`LAST ${range} DAYS`,72,196);
if(month&&year){ctx.textAlign='right';ctx.fillText(`${month} ${year}`,1008,196);ctx.textAlign='left';}
setTracking(ctx,'0px');
if(name){const nameSize=fitText(ctx,name.toUpperCase(),700,44,24);ctx.fillStyle=light;ctx.font=`900 ${nameSize}px "Arial Narrow","Avenir Next Condensed",-apple-system,sans-serif`;ctx.fillText(name.toUpperCase(),72,250);}
const leadLabel='LINES PUSHED',leadValue=formatStat(lead),hasCurrent=lead.available===true&&Number.isFinite(Number(lead.current)),heroValue=leadValue,condensed='"Arial Narrow","Avenir Next Condensed",-apple-system,sans-serif';
const commitValue=formatStat(commitStat),hasCommits=commitStat.available===true&&Number.isFinite(Number(commitStat.current));
ctx.fillStyle=gradientFill(ctx,72,0,180,0,[[0,cyan],[1,'rgba(0,188,235,.15)']]);ctx.fillRect(72,282,108,3);
ctx.fillStyle=gradientFill(ctx,1008,0,900,0,[[0,cyan],[1,'rgba(0,188,235,.15)']]);ctx.fillRect(900,282,108,3);
ctx.fillStyle='#9fb4bd';setTracking(ctx,'2.6px');ctx.font='900 17px -apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif';ctx.fillText(leadLabel,72,314);
ctx.textAlign='right';ctx.fillText('COMMITS',1008,314);ctx.textAlign='left';setTracking(ctx,'0px');
// The hero fits the left column only; the right 250px stays reserved for
// commits so a long line count can never run into it.
const heroSize=fitText(ctx,heroValue,660,176,100);ctx.fillStyle=gradientFill(ctx,58,340,58,486,[[0,'#ffffff'],[.5,'#dcf2fa'],[1,'#5cc4e6']]);ctx.font=`900 ${heroSize}px ${condensed}`;ctx.fillText(heroValue,58,486);
const commitSize=fitText(ctx,commitValue,250,96,44);ctx.fillStyle=gradientFill(ctx,1008,392,1008,486,[[0,'#dcf2fa'],[1,'#5cc4e6']]);ctx.font=`900 ${commitSize}px ${condensed}`;ctx.textAlign='right';ctx.fillText(commitValue,1008,486);ctx.textAlign='left';
const currentSummary=hasCurrent?`Current lines pushed ${leadValue} lines.`:'Current lines pushed unavailable.';
const commitSummary=hasCommits?`Commits pushed ${commitValue}.`:'Commits pushed unavailable.';
if(!hasCurrent){ctx.fillStyle='#82949c';ctx.font='900 15px -apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif';ctx.fillText('GIT EVIDENCE UNAVAILABLE',72,528);}
const activity=safeArray(payload&&payload.activity_days),active=activity.filter(day=>day&&day.recorded_session===true).length;
const groupSize=Math.max(1,Math.ceil(activity.length/30)),rhythm=activity.length>30?Array.from({length:30},(_,index)=>({recorded_session:activity.slice(index*groupSize,index*groupSize+groupSize).some(day=>day&&day.recorded_session===true)})):activity.slice(0,30),gap=7,cellWidth=(936-gap*Math.max(0,rhythm.length-1))/Math.max(1,rhythm.length);
rhythm.forEach((day,index)=>{
const on=day&&day.recorded_session===true,height=on?28+((index*19)%66):8,left=72+index*(cellWidth+gap),top=694-height;
ctx.fillStyle=on?gradientFill(ctx,left,top,left,694,[[0,sky],[.38,cyan],[1,'rgba(64,84,214,.72)']]):'rgba(159,180,189,.3)';
ctx.fillRect(left,top,cellWidth,height);
if(on){ctx.fillStyle='rgba(248,245,236,.9)';ctx.fillRect(left,top,cellWidth,2);}
});
ctx.fillStyle=gradientFill(ctx,24,0,1080,0,[[0,sky],[.32,cyan],[.72,cobalt],[1,violet]]);ctx.fillRect(24,710,1056,10);
const features=supportingStats(payload,{...options,leadId:lead.id}),labels={efficiency_change:'OUTPUT EFFICIENCY',speed_champion:'FASTEST MODEL',sessions:'AI SESSIONS'};
const streak=safeArray(payload&&payload.spotlights).find(stat=>stat&&stat.id==='build_streak'&&stat.available===true);
const spend=safeArray(payload&&payload.supporting).find(stat=>stat&&stat.id==='covered_spend');
const columns=[{value:`${active}/${range}`,label:'ACTIVE DAYS',sub:streak?`${formatStat(streak)} DAY STREAK`:''}];
features.forEach(stat=>{
const formatted=formatStat(stat),value=formatted==='—'?formatted:stat.id==='speed_champion'?`${formatted} TOK/S`:stat.id==='efficiency_change'?`${formatted.replace(/([KM])$/,' $1')}/$`:formatted;
const leader=options.includeUsage!==false&&stat.id==='speed_champion'?safeArray(stat.leaders).map(item=>String(item&&item.model||'').toUpperCase()).filter(Boolean)[0]||'':'';
columns.push({value,label:labels[stat.id]||String(stat.label||'STAT').toUpperCase(),sub:leader});
});
columns.push({value:formatSpend(spend),label:'EQUIV. SPEND',sub:''});
columns.forEach((column,index)=>{
const x=72+index*187;
if(index){ctx.fillStyle=gradientFill(ctx,0,754,0,896,[[0,'rgba(7,23,31,.03)'],[.45,'rgba(7,23,31,.17)'],[1,'rgba(7,23,31,.03)']]);ctx.fillRect(x-26,754,1,142);}
ctx.fillStyle=gradientFill(ctx,x,780,x,830,[[0,ink],[1,'#1d4a5c']]);ctx.font=`900 36px ${condensed}`;ctx.fillText(ellipsize(ctx,column.value,168),x,808);
ctx.fillStyle=muted;setTracking(ctx,'1.3px');ctx.font='900 12px -apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif';ctx.fillText(ellipsize(ctx,column.label,168),x,840);setTracking(ctx,'0px');
if(column.sub){ctx.fillStyle=cobalt;ctx.font='900 13px -apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif';ctx.fillText(ellipsize(ctx,column.sub,168),x,884);}
});
ctx.fillStyle=gradientFill(ctx,72,0,1008,0,[[0,cyan],[.3,ink],[1,'rgba(7,23,31,.12)']]);ctx.fillRect(72,928,936,3);
const usage=payload&&payload.usage||{},agentRows=safeArray(usage.agents),modelRows=safeArray(usage.models),hasUsage=agentRows.length>0||modelRows.length>0;
if(options.includeUsage!==false&&hasUsage){
if(agentRows.length)drawUsageGroup(ctx,'MOST-USED AGENT',agentRows,72,978,430,cyan,cobalt);
if(modelRows.length)drawUsageGroup(ctx,'GO-TO MODEL',modelRows,578,978,430,cobalt,violet);
if(agentRows.length&&modelRows.length){ctx.fillStyle=gradientFill(ctx,0,958,0,1188,[[0,'rgba(7,23,31,.03)'],[.45,'rgba(7,23,31,.17)'],[1,'rgba(7,23,31,.03)']]);ctx.fillRect(540,958,1,230);}
}
ctx.fillStyle=gradientFill(ctx,1000,1220,1080,1350,[[0,sky],[.5,cyan],[1,cobalt]]);ctx.fillRect(1000,1220,80,130);
ctx.fillStyle=gradientFill(ctx,0,0,1080,0,[[0,'rgba(0,188,235,.7)'],[.55,'rgba(127,219,242,.28)'],[1,'rgba(143,108,230,0)']]);ctx.fillRect(24,1220,1056,1);
ctx.fillStyle=light;setTracking(ctx,'1.2px');ctx.font='900 24px -apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif';ctx.fillText('BUILD YOURS →',72,1282);
ctx.fillStyle=sky;setTracking(ctx,'.6px');ctx.font='800 16px -apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif';ctx.fillText('github.com/splunk/token-meter',72,1316);setTracking(ctx,'0px');
const agentNames=options.includeUsage===false?[]:agentRows.map(row=>String(row.label||'')).filter(Boolean),modelNames=options.includeUsage===false?[]:modelRows.map(row=>`${row.runtime||'Unknown runtime'} ${row.label||''}`.trim()).filter(Boolean);
const sessions=features.find(stat=>stat.id==='sessions'),sessionsSummary=sessions&&sessions.available===true?`AI sessions ${formatStat(sessions)}.`:'AI sessions unavailable.';
const spendSummary=spend&&spend.available===true?`Covered equivalent spend ${formatSpend(spend)}, an estimate.`:'Covered equivalent spend unavailable.';
const rhythmSummary=groupSize>1?`Rhythm chart groups ${groupSize} days per bar.`:'Rhythm chart shows one day per bar.';
const identitySummary=name?`Name ${name}. `:'',summary=`${range}-day Builder Field Sheet. ${identitySummary}${currentSummary} ${commitSummary} ${active} active days. ${rhythmSummary} ${sessionsSummary} ${spendSummary}`;
const usageSummary=options.includeUsage===false?'Usage charts excluded.':!hasUsage?'Usage charts unavailable.':`Agent usage: ${agentNames.join(', ')||'unavailable'}. Models: ${modelNames.join(', ')||'unavailable'}; runtime-scoped model usage.`;
canvas.setAttribute('aria-label',`${summary} ${usageSummary}`);
return {summary};
}
function drawPlaceholder(canvas,label){
canvas.width=1080;canvas.height=1350;const ctx=canvas.getContext('2d',{willReadFrequently:true});
ctx.fillStyle=gradientFill(ctx,0,0,0,1350,[[0,'#f6f3ea'],[1,'#e8e4d8']]);ctx.fillRect(0,0,1080,1350);
ctx.fillStyle=gradientFill(ctx,0,0,0,1350,[[0,'#8ee9f2'],[.3,'#00bceb'],[.7,'#4054d6'],[1,'#8f6ce6']]);ctx.fillRect(0,0,20,1350);
ctx.fillStyle='#10191d';setTracking(ctx,'1.4px');ctx.font='900 30px -apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif';ctx.fillText('TOKEN METER',82,88);setTracking(ctx,'0px');
ctx.fillStyle='#758087';setTracking(ctx,'2px');ctx.font='900 18px -apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif';ctx.fillText(String(label||'PREVIEW'),82,180);setTracking(ctx,'0px');
canvas.setAttribute('aria-label',`Builder recap preview ${String(label||'unavailable').toLowerCase()}`);
}
function setStatus(message,kind=''){const status=$('status');status.textContent=message;status.dataset.kind=kind;}
function builderOptions(){return {name:builderRecapState.name,includeUsage:builderRecapState.includeUsage,logo:$('performance-card-logo')};}
function showLogoFailure(){
builderRecapState.logoError=true;builderRecapState.rendered=false;$('download').disabled=true;
if(builderRecapState.loading||builderRecapState.error||!builderRecapState.payload)return false;
drawPlaceholder($('poster'),'BRAND ASSET UNAVAILABLE');$('retry').hidden=false;setStatus('Local brand asset unavailable. Try again.','error');return false;
}
function retryLogo(){
const logo=$('performance-card-logo'),source=logo&&logo.getAttribute('src');if(!logo||!source)return showLogoFailure();
builderRecapState.logoError=false;builderRecapState.rendered=false;$('download').disabled=true;$('retry').hidden=true;drawPlaceholder($('poster'),'LOADING BRAND ASSET');setStatus('Loading local brand asset…');
logo.removeAttribute('src');logo.src=source;return false;
}
function handleLogoLoad(){builderRecapState.logoError=false;if(builderRecapState.payload&&!builderRecapState.error)return renderPreview();return false;}
function hasActivity(payload){const sessionActivity=safeArray(payload&&payload.activity_days).some(day=>day&&day.recorded_session===true),lines=safeArray(payload&&payload.spotlights).find(stat=>stat&&stat.id==='lines_pushed');return sessionActivity||Boolean(lines&&lines.available===true&&Number.isFinite(Number(lines.current))&&Number(lines.current)>0);}
function renderControls(payload){
document.querySelectorAll('[data-range]').forEach(button=>button.classList.toggle('on',Number(button.dataset.range)===builderRecapState.range));
$('builder-name').value=builderRecapState.name;
const usage=payload&&payload.usage||{},usageAvailable=safeArray(usage.agents).length>0||safeArray(usage.models).length>0;$('include-usage').disabled=!usageAvailable;$('include-usage').checked=builderRecapState.includeUsage&&usageAvailable;
}
function renderPreview(){
const download=$('download');download.disabled=true;builderRecapState.rendered=false;
if(builderRecapState.loading||builderRecapState.error||!builderRecapState.payload)return false;
if(!hasActivity(builderRecapState.payload)){drawPlaceholder($('poster'),'NO RECORDED ACTIVITY');setStatus('No recorded activity in this period.');return false;}
const options=builderOptions(),logo=options.logo;if(!logo)return showLogoFailure();if(!logo.complete){setStatus('Loading local brand asset…');return false;}if(Number(logo.naturalWidth)<=0)return showLogoFailure();
try{drawBuilderRecap($('poster'),builderRecapState.payload,options);builderRecapState.logoError=false;builderRecapState.rendered=true;download.disabled=false;$('retry').hidden=true;setStatus('');return true;}catch(error){builderRecapState.error='preview';drawPlaceholder($('poster'),'PREVIEW UNAVAILABLE');setStatus('Preview unavailable. Try again.','error');$('retry').hidden=false;return false;}
}
async function loadRecap(range=30){
range=[7,30,90].includes(Number(range))?Number(range):30;builderRecapState.range=range;
if(builderRecapState.controller)builderRecapState.controller.abort();
const request=++builderRecapState.request,controller=new AbortController();builderRecapState.controller=controller;builderRecapState.loading=true;builderRecapState.error='';builderRecapState.rendered=false;$('download').disabled=true;$('retry').hidden=true;renderControls(builderRecapState.payload);drawPlaceholder($('poster'),'LOADING');setStatus('Loading local recap evidence…');
try{const response=await fetch(`/builder-recap?range=${range}`,{cache:'no-store',signal:controller.signal});if(!response.ok)throw new Error('request');const payload=await response.json();if(request!==builderRecapState.request||controller.signal.aborted)return false;if(!payload||payload.ok!==true)throw new Error('payload');builderRecapState.payload=payload;builderRecapState.loading=false;renderControls(payload);return renderPreview();}
catch(error){if(request!==builderRecapState.request||controller.signal.aborted||error&&error.name==='AbortError')return false;builderRecapState.loading=false;builderRecapState.error='request';builderRecapState.payload=null;renderControls(null);drawPlaceholder($('poster'),'PREVIEW UNAVAILABLE');$('retry').hidden=false;setStatus('Unable to load the builder recap. Try again.','error');return false;}
}
function resetRecap(){builderRecapState.name='';builderRecapState.includeUsage=true;$('builder-name').value='';$('include-usage').checked=true;return loadRecap(30);}
function recapFilename(){const day=String(builderRecapState.payload&&builderRecapState.payload.current&&builderRecapState.payload.current.end_day||new Date().toISOString().slice(0,10));return `token-meter-builder-field-sheet-${builderRecapState.range}d-${day}.png`;}
function downloadRecap(){
if(!builderRecapState.rendered||builderRecapState.loading||builderRecapState.error){$('download').disabled=true;return;}
const canvas=$('poster');canvas.toBlob(blob=>{if(!blob){builderRecapState.rendered=false;$('download').disabled=true;setStatus('PNG export failed. Try again.','error');return;}let url='';try{url=URL.createObjectURL(blob);const link=document.createElement('a');link.href=url;link.download=recapFilename();link.click();setStatus('PNG downloaded.','success');setTimeout(()=>URL.revokeObjectURL(url),0);}catch(error){if(url)URL.revokeObjectURL(url);builderRecapState.rendered=false;$('download').disabled=true;setStatus('PNG export failed. Try again.','error');}},'image/png');
}
document.querySelectorAll('[data-range]').forEach(button=>button.addEventListener('click',()=>loadRecap(Number(button.dataset.range))));
$('builder-name').addEventListener('input',event=>{builderRecapState.name=sanitizeBuilderName(event.target.value);renderPreview();});
$('include-usage').addEventListener('change',event=>{builderRecapState.includeUsage=event.target.checked;renderControls(builderRecapState.payload);renderPreview();});
$('reset').addEventListener('click',resetRecap);$('retry').addEventListener('click',()=>builderRecapState.logoError&&!builderRecapState.error?retryLogo():loadRecap(builderRecapState.range));$('download').addEventListener('click',downloadRecap);
$('performance-card-logo').addEventListener('load',handleLogoLoad);$('performance-card-logo').addEventListener('error',showLogoFailure);
drawPlaceholder($('poster'),'LOADING');loadRecap(30);
</script>
</body>
</html>