@ipv9/tokentracker-cli 0.39.22 → 0.39.23

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.
Files changed (44) hide show
  1. package/dashboard/dist/assets/ActivityHeatmap-DPWjzqNf.js +42 -0
  2. package/dashboard/dist/assets/{Card-Dm-vV5fT.js → Card-B6l7DFB6.js} +1 -1
  3. package/dashboard/dist/assets/DashboardPage-COqvftnM.js +19 -0
  4. package/dashboard/dist/assets/{DevicePage-DL2Evorm.js → DevicePage-BTX6oiNV.js} +1 -1
  5. package/dashboard/dist/assets/{DialogTitle-BGPDFPkc.js → DialogTitle-Bqdf6lRl.js} +1 -1
  6. package/dashboard/dist/assets/{FadeIn-CQ_YmrFw.js → FadeIn-C0eGg9YT.js} +1 -1
  7. package/dashboard/dist/assets/{InsforgeUserHeaderControls-YPHdtTTd.js → InsforgeUserHeaderControls-BvVeuzmy.js} +1 -1
  8. package/dashboard/dist/assets/{IpCheckPage-BVvV3tEl.js → IpCheckPage-BCxj95za.js} +1 -1
  9. package/dashboard/dist/assets/{LandingPage-C53O19r8.js → LandingPage-DkW_-IUq.js} +1 -1
  10. package/dashboard/dist/assets/{LeaderboardAvatar-vmpdj2pR.js → LeaderboardAvatar-CdvMXLQJ.js} +1 -1
  11. package/dashboard/dist/assets/{LeaderboardPage-no6kgyFi.js → LeaderboardPage-CEsp6t6E.js} +3 -3
  12. package/dashboard/dist/assets/{LeaderboardProfileModal-fTDvlvZN.js → LeaderboardProfileModal--rW0KlHw.js} +1 -1
  13. package/dashboard/dist/assets/LeaderboardProfilePage-CxUYiGeW.js +1 -0
  14. package/dashboard/dist/assets/{LimitsPage-DcTgsN6X.js → LimitsPage-BXMCGGdO.js} +2 -2
  15. package/dashboard/dist/assets/{LocalOnlyNotice-VeVP185X.js → LocalOnlyNotice-CaueKPwt.js} +1 -1
  16. package/dashboard/dist/assets/{LoginPage-CY-qZZy0.js → LoginPage-DJClCMkk.js} +1 -1
  17. package/dashboard/dist/assets/{PopoverPopup-be0Sxsht.js → PopoverPopup-ClB7lBvx.js} +1 -1
  18. package/dashboard/dist/assets/{Select-DNNgvQEV.js → Select-DCwfhdKp.js} +1 -1
  19. package/dashboard/dist/assets/{SelectItemText-CBPoUy8w.js → SelectItemText-BroYKgBb.js} +1 -1
  20. package/dashboard/dist/assets/SettingsPage-hFiQM-_N.js +1 -0
  21. package/dashboard/dist/assets/{SkillsPage-DCb1zS1J.js → SkillsPage-DmJIQF8f.js} +1 -1
  22. package/dashboard/dist/assets/{WidgetsPage-BRVmAgq1.js → WidgetsPage-CO8R-tlm.js} +1 -1
  23. package/dashboard/dist/assets/{WrappedPage-BcuXD45L.js → WrappedPage-CCpOup2F.js} +1 -1
  24. package/dashboard/dist/assets/{arrow-up-right-Cyh3-4de.js → arrow-up-right-JK3-vzAg.js} +1 -1
  25. package/dashboard/dist/assets/{download-C3eW5ynF.js → download-BZ6S1rdH.js} +1 -1
  26. package/dashboard/dist/assets/{info-CSAnLIAh.js → info-35FGjeST.js} +1 -1
  27. package/dashboard/dist/assets/main-B4M18BsA.css +1 -0
  28. package/dashboard/dist/assets/{main-BAdX_7hM.js → main-Bs0wIGxU.js} +2 -2
  29. package/dashboard/dist/assets/{use-limits-display-prefs-BKBMjWGn.js → use-limits-display-prefs-BnIRF1GH.js} +1 -1
  30. package/dashboard/dist/assets/{use-native-settings-CQuCbPc9.js → use-native-settings-CHMWJ78h.js} +1 -1
  31. package/dashboard/dist/assets/{use-spring-BTelsGuG.js → use-spring-D6NY3otb.js} +1 -1
  32. package/dashboard/dist/assets/{use-usage-limits-a7G_-yhi.js → use-usage-limits-BY-Kdrvn.js} +1 -1
  33. package/dashboard/dist/assets/{useCurrency-BnMdRysu.js → useCurrency--TEilWkm.js} +1 -1
  34. package/dashboard/dist/assets/{useOpenInteractionType-DunsezWq.js → useOpenInteractionType-uDQvb3tj.js} +1 -1
  35. package/dashboard/dist/index.html +2 -2
  36. package/dashboard/dist/share.html +2 -2
  37. package/package.json +1 -1
  38. package/src/lib/pricing/seed-snapshot.json +1 -1
  39. package/src/lib/rollout.js +153 -27
  40. package/dashboard/dist/assets/ActivityHeatmap-DqpmSECi.js +0 -42
  41. package/dashboard/dist/assets/DashboardPage-CcTjnozw.js +0 -19
  42. package/dashboard/dist/assets/LeaderboardProfilePage-CAFncZz7.js +0 -1
  43. package/dashboard/dist/assets/SettingsPage-DxP5qY9l.js +0 -1
  44. package/dashboard/dist/assets/main-fhNSX8yD.css +0 -1
@@ -49,6 +49,29 @@ async function listClaudeProjectFiles(projectsDir) {
49
49
  return out;
50
50
  }
51
51
 
52
+ // Fingerprint the first bytes of a file so rotation is detected even when the
53
+ // OS reuses the inode after unlink+recreate (Linux logrotate). Returns "" on
54
+ // any error so callers treat "no fingerprint" as "not changed". Also returns
55
+ // "" when the file is shorter than the fingerprint window: for a sub-256B
56
+ // file that is still being appended to, the window would extend into the
57
+ // not-yet-written append zone, so plain growth (not rotation) would shift
58
+ // the hash and produce a false "rotated" signal.
59
+ function readFileHeadSignature(filePath) {
60
+ try {
61
+ const fd = fssync.openSync(filePath, "r");
62
+ try {
63
+ const buf = Buffer.alloc(256);
64
+ const bytes = fssync.readSync(fd, buf, 0, 256, 0);
65
+ if (bytes < 256) return "";
66
+ return crypto.createHash("sha1").update(buf.subarray(0, bytes)).digest("hex");
67
+ } finally {
68
+ fssync.closeSync(fd);
69
+ }
70
+ } catch {
71
+ return "";
72
+ }
73
+ }
74
+
52
75
  async function listGeminiSessionFiles(tmpDir) {
53
76
  const out = [];
54
77
  const roots = await safeReadDir(tmpDir);
@@ -116,9 +139,18 @@ async function parseRolloutIncremental({
116
139
  const key = filePath;
117
140
  const prev = cursors.files[key] || null;
118
141
  const inode = st.ino || 0;
119
- const startOffset = prev && prev.inode === inode ? prev.offset || 0 : 0;
120
- const lastTotal = prev && prev.inode === inode ? prev.lastTotal || null : null;
121
- const lastModel = prev && prev.inode === inode ? prev.lastModel || null : null;
142
+ // Reuse the saved offset only when the inode matches AND the head
143
+ // fingerprint is unchanged AND the file hasn't shrunk below the saved
144
+ // offset any of those signals flipping means the file was rotated
145
+ // (e.g. Linux logrotate reuses the inode after unlink+recreate).
146
+ const headSig = readFileHeadSignature(filePath);
147
+ const headChanged =
148
+ !!prev && typeof prev.head === "string" && prev.head !== "" && prev.head !== headSig;
149
+ const shrunk = !!prev && typeof prev.offset === "number" && st.size < prev.offset;
150
+ const sameFile = !!prev && prev.inode === inode && !headChanged && !shrunk;
151
+ const startOffset = sameFile ? prev.offset || 0 : 0;
152
+ const lastTotal = sameFile ? prev.lastTotal || null : null;
153
+ const lastModel = sameFile ? prev.lastModel || null : null;
122
154
 
123
155
  const projectContext = projectEnabled
124
156
  ? await resolveProjectContextForFile({
@@ -154,6 +186,7 @@ async function parseRolloutIncremental({
154
186
  offset: result.endOffset,
155
187
  lastTotal: result.lastTotal,
156
188
  lastModel: result.lastModel,
189
+ head: headSig,
157
190
  updatedAt: new Date().toISOString(),
158
191
  };
159
192
 
@@ -233,7 +266,16 @@ async function parseClaudeIncremental({
233
266
  const key = filePath;
234
267
  const prev = cursors.files[key] || null;
235
268
  const inode = st.ino || 0;
236
- const startOffset = prev && prev.inode === inode ? prev.offset || 0 : 0;
269
+ // Reuse the saved offset only when the inode matches AND the head
270
+ // fingerprint is unchanged AND the file hasn't shrunk below the saved
271
+ // offset — a rotator that reuses the inode (Linux logrotate) would
272
+ // otherwise leave the old offset stuck and skip the new prefix.
273
+ const headSig = readFileHeadSignature(filePath);
274
+ const headChanged =
275
+ !!prev && typeof prev.head === "string" && prev.head !== "" && prev.head !== headSig;
276
+ const shrunk = !!prev && typeof prev.offset === "number" && st.size < prev.offset;
277
+ const sameFile = !!prev && prev.inode === inode && !headChanged && !shrunk;
278
+ const startOffset = sameFile ? prev.offset || 0 : 0;
237
279
 
238
280
  const projectContext = projectEnabled
239
281
  ? await resolveProjectContextForFile({
@@ -263,6 +305,7 @@ async function parseClaudeIncremental({
263
305
  cursors.files[key] = {
264
306
  inode,
265
307
  offset: result.endOffset,
308
+ head: headSig,
266
309
  updatedAt: new Date().toISOString(),
267
310
  };
268
311
 
@@ -342,9 +385,18 @@ async function parseGeminiIncremental({
342
385
  const key = filePath;
343
386
  const prev = cursors.files[key] || null;
344
387
  const inode = st.ino || 0;
345
- let startIndex = prev && prev.inode === inode ? Number(prev.lastIndex || -1) : -1;
346
- let lastTotals = prev && prev.inode === inode ? prev.lastTotals || null : null;
347
- let lastModel = prev && prev.inode === inode ? prev.lastModel || null : null;
388
+ // Reuse the saved message index only when the inode matches AND the head
389
+ // fingerprint is unchanged AND the file hasn't shrunk below its last
390
+ // recorded size a rotator that reuses the inode (Linux logrotate)
391
+ // would otherwise leave the old index stuck and skip replayed messages.
392
+ const headSig = readFileHeadSignature(filePath);
393
+ const headChanged =
394
+ !!prev && typeof prev.head === "string" && prev.head !== "" && prev.head !== headSig;
395
+ const shrunk = !!prev && typeof prev.size === "number" && st.size < prev.size;
396
+ const sameFile = !!prev && prev.inode === inode && !headChanged && !shrunk;
397
+ let startIndex = sameFile ? Number(prev.lastIndex || -1) : -1;
398
+ let lastTotals = sameFile ? prev.lastTotals || null : null;
399
+ let lastModel = sameFile ? prev.lastModel || null : null;
348
400
 
349
401
  const projectContext = projectEnabled
350
402
  ? await resolveProjectContextForFile({
@@ -374,9 +426,11 @@ async function parseGeminiIncremental({
374
426
 
375
427
  cursors.files[key] = {
376
428
  inode,
429
+ size: st.size,
377
430
  lastIndex: result.lastIndex,
378
431
  lastTotals: result.lastTotals,
379
432
  lastModel: result.lastModel,
433
+ head: headSig,
380
434
  updatedAt: new Date().toISOString(),
381
435
  };
382
436
 
@@ -456,8 +510,18 @@ async function parseOpencodeIncremental({
456
510
  const inode = st.ino || 0;
457
511
  const size = Number.isFinite(st.size) ? st.size : 0;
458
512
  const mtimeMs = Number.isFinite(st.mtimeMs) ? st.mtimeMs : 0;
513
+ // Head fingerprint guards against a rotator that reuses the inode (Linux
514
+ // logrotate): if the head changed we must never treat the file as
515
+ // "unchanged", even when inode/size/mtimeMs happen to match.
516
+ const headSig = readFileHeadSignature(filePath);
517
+ const headChanged =
518
+ !!prev && typeof prev.head === "string" && prev.head !== "" && prev.head !== headSig;
459
519
  const unchanged =
460
- prev && prev.inode === inode && prev.size === size && prev.mtimeMs === mtimeMs;
520
+ prev &&
521
+ prev.inode === inode &&
522
+ prev.size === size &&
523
+ prev.mtimeMs === mtimeMs &&
524
+ !headChanged;
461
525
  if (unchanged) {
462
526
  filesProcessed += 1;
463
527
  if (cb) {
@@ -510,6 +574,7 @@ async function parseOpencodeIncremental({
510
574
  mtimeMs,
511
575
  lastTotals: result.lastTotals,
512
576
  messageKey: result.messageKey || null,
577
+ head: headSig,
513
578
  updatedAt: new Date().toISOString(),
514
579
  };
515
580
 
@@ -591,7 +656,16 @@ async function parseOpenclawIncremental({
591
656
  const key = filePath;
592
657
  const prev = cursors.files[key] || null;
593
658
  const inode = st.ino || 0;
594
- const startOffset = prev && prev.inode === inode ? prev.offset || 0 : 0;
659
+ // Reuse the saved offset only when the inode matches AND the head
660
+ // fingerprint is unchanged AND the file hasn't shrunk below the saved
661
+ // offset — a rotator that reuses the inode (Linux logrotate) would
662
+ // otherwise leave the old offset stuck and skip the new prefix.
663
+ const headSig = readFileHeadSignature(filePath);
664
+ const headChanged =
665
+ !!prev && typeof prev.head === "string" && prev.head !== "" && prev.head !== headSig;
666
+ const shrunk = !!prev && typeof prev.offset === "number" && st.size < prev.offset;
667
+ const sameFile = !!prev && prev.inode === inode && !headChanged && !shrunk;
668
+ const startOffset = sameFile ? prev.offset || 0 : 0;
595
669
 
596
670
  const result = await parseOpenclawSessionFile({
597
671
  filePath,
@@ -606,6 +680,7 @@ async function parseOpenclawIncremental({
606
680
  cursors.files[key] = {
607
681
  inode,
608
682
  offset: result.endOffset,
683
+ head: headSig,
609
684
  updatedAt: new Date().toISOString(),
610
685
  };
611
686
 
@@ -4205,8 +4280,11 @@ async function parseKimiIncremental({ wireFiles, cursors, queuePath, onProgress,
4205
4280
  const prevEntry = fileOffsets[filePath] || {};
4206
4281
  const prevSize = Number(prevEntry.size) || 0;
4207
4282
  const prevIno = prevEntry.ino;
4283
+ const headSig = readFileHeadSignature(filePath);
4208
4284
  const inodeChanged = typeof prevIno === "number" && prevIno !== stat.ino;
4209
- const startOffset = stat.size < prevSize || inodeChanged ? 0 : prevSize;
4285
+ const headChanged =
4286
+ typeof prevEntry.head === "string" && prevEntry.head !== "" && prevEntry.head !== headSig;
4287
+ const startOffset = stat.size < prevSize || inodeChanged || headChanged ? 0 : prevSize;
4210
4288
  if (stat.size <= startOffset) continue;
4211
4289
 
4212
4290
  let stream;
@@ -4275,7 +4353,12 @@ async function parseKimiIncremental({ wireFiles, cursors, queuePath, onProgress,
4275
4353
 
4276
4354
  let postStat = stat;
4277
4355
  try { postStat = fssync.statSync(filePath); } catch {}
4278
- fileOffsets[filePath] = { size: postStat.size, mtimeMs: postStat.mtimeMs, ino: postStat.ino };
4356
+ fileOffsets[filePath] = {
4357
+ size: postStat.size,
4358
+ mtimeMs: postStat.mtimeMs,
4359
+ ino: postStat.ino,
4360
+ head: readFileHeadSignature(filePath),
4361
+ };
4279
4362
  }
4280
4363
 
4281
4364
  // Cap seenIds to last 10k to bound cursor state size
@@ -4384,8 +4467,11 @@ async function parseKimiCodeIncremental({ wireFiles, cursors, queuePath, onProgr
4384
4467
  const prevEntry = fileOffsets[filePath] || {};
4385
4468
  const prevSize = Number(prevEntry.size) || 0;
4386
4469
  const prevIno = prevEntry.ino;
4470
+ const headSig = readFileHeadSignature(filePath);
4387
4471
  const inodeChanged = typeof prevIno === "number" && prevIno !== stat.ino;
4388
- const startOffset = stat.size < prevSize || inodeChanged ? 0 : prevSize;
4472
+ const headChanged =
4473
+ typeof prevEntry.head === "string" && prevEntry.head !== "" && prevEntry.head !== headSig;
4474
+ const startOffset = stat.size < prevSize || inodeChanged || headChanged ? 0 : prevSize;
4389
4475
  // Model is declared in a `config.update` near the file head; persist it on
4390
4476
  // the cursor so incremental resumes (which start past that line) keep it.
4391
4477
  let fileModel = (typeof prevEntry.model === "string" && prevEntry.model) || fallbackModel;
@@ -4480,7 +4566,13 @@ async function parseKimiCodeIncremental({ wireFiles, cursors, queuePath, onProgr
4480
4566
 
4481
4567
  let postStat = stat;
4482
4568
  try { postStat = fssync.statSync(filePath); } catch {}
4483
- fileOffsets[filePath] = { size: postStat.size, mtimeMs: postStat.mtimeMs, ino: postStat.ino, model: fileModel };
4569
+ fileOffsets[filePath] = {
4570
+ size: postStat.size,
4571
+ mtimeMs: postStat.mtimeMs,
4572
+ ino: postStat.ino,
4573
+ model: fileModel,
4574
+ head: readFileHeadSignature(filePath),
4575
+ };
4484
4576
  }
4485
4577
 
4486
4578
  const seenArr = Array.from(seenIds);
@@ -4619,10 +4711,14 @@ async function parseCodebuddyIncremental({
4619
4711
  const prevEntry = fileOffsets[filePath] || {};
4620
4712
  const prevSize = Number(prevEntry.size) || 0;
4621
4713
  const prevIno = prevEntry.ino;
4622
- // Re-read from start if file shrunk (truncate/rewrite) or inode changed
4623
- // (file deleted + recreated). Otherwise pick up after the last read offset.
4714
+ const headSig = readFileHeadSignature(filePath);
4715
+ // Re-read from start if file shrunk (truncate/rewrite), inode changed
4716
+ // (file deleted + recreated), or the head fingerprint changed (rotator
4717
+ // reused the inode). Otherwise pick up after the last read offset.
4624
4718
  const inodeChanged = typeof prevIno === "number" && prevIno !== stat.ino;
4625
- const startOffset = stat.size < prevSize || inodeChanged ? 0 : prevSize;
4719
+ const headChanged =
4720
+ typeof prevEntry.head === "string" && prevEntry.head !== "" && prevEntry.head !== headSig;
4721
+ const startOffset = stat.size < prevSize || inodeChanged || headChanged ? 0 : prevSize;
4626
4722
  if (stat.size <= startOffset) continue;
4627
4723
 
4628
4724
  let stream;
@@ -4748,6 +4844,7 @@ async function parseCodebuddyIncremental({
4748
4844
  size: postStat.size,
4749
4845
  mtimeMs: postStat.mtimeMs,
4750
4846
  ino: postStat.ino,
4847
+ head: readFileHeadSignature(filePath),
4751
4848
  };
4752
4849
  }
4753
4850
 
@@ -6508,9 +6605,13 @@ async function parseOmpIncremental({
6508
6605
  const prevEntry = fileOffsets[filePath] || {};
6509
6606
  const prevSize = Number(prevEntry.size) || 0;
6510
6607
  const prevIno = prevEntry.ino;
6511
- // Re-read from start if file shrunk (truncate/rewrite) or inode changed.
6608
+ const headSig = readFileHeadSignature(filePath);
6609
+ // Re-read from start if file shrunk (truncate/rewrite), inode changed, or
6610
+ // the head fingerprint changed (rotator reused the inode).
6512
6611
  const inodeChanged = typeof prevIno === "number" && prevIno !== stat.ino;
6513
- const startOffset = stat.size < prevSize || inodeChanged ? 0 : prevSize;
6612
+ const headChanged =
6613
+ typeof prevEntry.head === "string" && prevEntry.head !== "" && prevEntry.head !== headSig;
6614
+ const startOffset = stat.size < prevSize || inodeChanged || headChanged ? 0 : prevSize;
6514
6615
  if (stat.size <= startOffset) continue;
6515
6616
 
6516
6617
  let stream;
@@ -6616,6 +6717,7 @@ async function parseOmpIncremental({
6616
6717
  size: postStat.size,
6617
6718
  mtimeMs: postStat.mtimeMs,
6618
6719
  ino: postStat.ino,
6720
+ head: readFileHeadSignature(filePath),
6619
6721
  };
6620
6722
  }
6621
6723
 
@@ -6756,8 +6858,11 @@ async function parsePiIncremental({
6756
6858
  const prevEntry = fileOffsets[filePath] || {};
6757
6859
  const prevSize = Number(prevEntry.size) || 0;
6758
6860
  const prevIno = prevEntry.ino;
6861
+ const headSig = readFileHeadSignature(filePath);
6759
6862
  const inodeChanged = typeof prevIno === "number" && prevIno !== stat.ino;
6760
- const startOffset = stat.size < prevSize || inodeChanged ? 0 : prevSize;
6863
+ const headChanged =
6864
+ typeof prevEntry.head === "string" && prevEntry.head !== "" && prevEntry.head !== headSig;
6865
+ const startOffset = stat.size < prevSize || inodeChanged || headChanged ? 0 : prevSize;
6761
6866
  if (stat.size <= startOffset) continue;
6762
6867
 
6763
6868
  let stream;
@@ -6855,6 +6960,7 @@ async function parsePiIncremental({
6855
6960
  size: postStat.size,
6856
6961
  mtimeMs: postStat.mtimeMs,
6857
6962
  ino: postStat.ino,
6963
+ head: readFileHeadSignature(filePath),
6858
6964
  };
6859
6965
  }
6860
6966
 
@@ -7400,12 +7506,15 @@ async function parseCopilotIncremental({ otelPaths, cursors, queuePath, onProgre
7400
7506
  const prevEntry = fileOffsets[filePath] || {};
7401
7507
  const prevSize = Number(prevEntry.size) || 0;
7402
7508
  const prevIno = prevEntry.ino;
7403
- // Re-read from start if (a) file shrunk (truncate/rewrite in place) or
7404
- // (b) inode changed (rotator deleted + recreated at same path). Without
7405
- // the inode check, a rotator producing a same-or-larger file would leave
7406
- // the old offset stuck and skip the new file's prefix forever.
7509
+ const headSig = readFileHeadSignature(filePath);
7510
+ // Re-read from start if (a) file shrunk (truncate/rewrite in place),
7511
+ // (b) inode changed (rotator deleted + recreated at same path), or
7512
+ // (c) the head fingerprint changed (rotator reused the inode Linux
7513
+ // logrotate — and produced a same-or-larger file, so (a)/(b) miss it).
7407
7514
  const inodeChanged = typeof prevIno === "number" && prevIno !== stat.ino;
7408
- const startOffset = stat.size < prevSize || inodeChanged ? 0 : prevSize;
7515
+ const headChanged =
7516
+ typeof prevEntry.head === "string" && prevEntry.head !== "" && prevEntry.head !== headSig;
7517
+ const startOffset = stat.size < prevSize || inodeChanged || headChanged ? 0 : prevSize;
7409
7518
  if (stat.size <= startOffset) continue;
7410
7519
 
7411
7520
  let stream;
@@ -7510,7 +7619,12 @@ async function parseCopilotIncremental({ otelPaths, cursors, queuePath, onProgre
7510
7619
  try {
7511
7620
  postStat = fssync.statSync(filePath);
7512
7621
  } catch (_e) {}
7513
- fileOffsets[filePath] = { size: postStat.size, mtimeMs: postStat.mtimeMs, ino: postStat.ino };
7622
+ fileOffsets[filePath] = {
7623
+ size: postStat.size,
7624
+ mtimeMs: postStat.mtimeMs,
7625
+ ino: postStat.ino,
7626
+ head: readFileHeadSignature(filePath),
7627
+ };
7514
7628
  }
7515
7629
 
7516
7630
  // Cap dedup set to last 10k IDs to bound state size
@@ -8016,9 +8130,20 @@ async function parseAntigravityIncremental({
8016
8130
  const inode = st.ino || 0;
8017
8131
  const size = Number.isFinite(st.size) ? st.size : 0;
8018
8132
  const mtimeMs = Number.isFinite(st.mtimeMs) ? st.mtimeMs : 0;
8133
+ // Head fingerprint + shrink guard against a rotator that reuses the
8134
+ // inode (Linux logrotate): either signal flipping means the cached
8135
+ // progress (unchanged skip, or lastLine/context resume) must not be reused.
8136
+ const headSig = readFileHeadSignature(filePath);
8137
+ const headChanged =
8138
+ !!prev && typeof prev.head === "string" && prev.head !== "" && prev.head !== headSig;
8139
+ const shrunk = !!prev && typeof prev.size === "number" && size < prev.size;
8019
8140
 
8020
8141
  const unchanged =
8021
- prev && prev.inode === inode && prev.size === size && prev.mtimeMs === mtimeMs;
8142
+ prev &&
8143
+ prev.inode === inode &&
8144
+ prev.size === size &&
8145
+ prev.mtimeMs === mtimeMs &&
8146
+ !headChanged;
8022
8147
  if (unchanged) {
8023
8148
  filesProcessed += 1;
8024
8149
  if (cb) {
@@ -8034,7 +8159,7 @@ async function parseAntigravityIncremental({
8034
8159
  continue;
8035
8160
  }
8036
8161
 
8037
- const sameFile = prev && prev.inode === inode;
8162
+ const sameFile = !!prev && prev.inode === inode && !headChanged && !shrunk;
8038
8163
  const lastLine = sameFile ? Number(prev.lastLine || 0) : 0;
8039
8164
  const initialContextTokens = sameFile ? Number(prev.contextTokens || 0) : 0;
8040
8165
  const initialPrevContext = sameFile ? Number(prev.previousContextTokens || 0) : 0;
@@ -8075,6 +8200,7 @@ async function parseAntigravityIncremental({
8075
8200
  contextTokens: result.contextTokens,
8076
8201
  previousContextTokens: result.previousContextTokens,
8077
8202
  currentModel: result.currentModel,
8203
+ head: headSig,
8078
8204
  updatedAt: new Date().toISOString(),
8079
8205
  };
8080
8206
 
@@ -1,42 +0,0 @@
1
- import{c as ue,r as i,j as e,A as n,aw as We,l as Fe,C as He,X as Ie,aj as Ge,D as Oe}from"./main-BAdX_7hM.js";import{u as qe}from"./useCurrency-BnMdRysu.js";import{I as Ze}from"./info-CSAnLIAh.js";const Ke=[["path",{d:"M15 3h6v6",key:"1q9fwt"}],["path",{d:"m21 3-7 7",key:"1l2asr"}],["path",{d:"m3 21 7-7",key:"tjx5ai"}],["path",{d:"M9 21H3v-6",key:"wtvkvv"}]],Ve=ue("maximize-2",Ke);const Je=[["rect",{x:"14",y:"3",width:"5",height:"18",rx:"1",key:"kaeet6"}],["rect",{x:"5",y:"3",width:"5",height:"18",rx:"1",key:"1wsw3u"}]],Qe=ue("pause",Je);const et=[["path",{d:"M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z",key:"10ikf1"}]],tt=ue("play",et);const at=[["path",{d:"M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8",key:"1357e3"}],["path",{d:"M3 3v5h5",key:"1xhq8a"}]],rt=ue("rotate-ccw",at);const ot=[["path",{d:"M12 19h8",key:"baeox8"}],["path",{d:"m4 17 6-6-6-6",key:"1yngyt"}]],nt=ue("terminal",ot),Q={emerald:{light:["#ebedf0","#a7f3d0","#6ee7b7","#34d399","#10b981"],dark:["#2d333b","#065f46","#059669","#10b981","#34d399"],gridColor:{light:"rgba(16, 185, 129, 0.12)",dark:"rgba(52, 211, 153, 0.08)"}},ocean:{light:["#f1f5f9","#93c5fd","#60a5fa","#3b82f6","#1d4ed8"],dark:["#1e293b","#1e3a8a","#2563eb","#3b82f6","#60a5fa"],gridColor:{light:"rgba(59, 130, 246, 0.12)",dark:"rgba(96, 165, 250, 0.08)"}},neon:{light:["#faf5ff","#ebd5ff","#c084fc","#a855f7","#7e22ce"],dark:["#2e1065","#581c87","#8b5cf6","#a855f7","#c084fc"],gridColor:{light:"rgba(168, 85, 247, 0.12)",dark:"rgba(192, 132, 252, 0.08)"}},amber:{light:["#fffbeb","#fde68a","#f59e0b","#d97706","#b45309"],dark:["#451a03","#78350f","#b45309","#d97706","#f59e0b"],gridColor:{light:"rgba(245, 158, 11, 0.12)",dark:"rgba(245, 158, 11, 0.08)"}}};function st(l,x){const p=/^#([0-9a-f]{6})$/i.exec(l);if(!p)return l;const c=parseInt(p[1],16),z=c>>16&255,P=c>>8&255,$=c&255,S=E=>Math.max(0,Math.min(255,Math.round(E*x)));return`rgb(${S(z)}, ${S(P)}, ${S($)})`}function O(l,x,p,c,z){const P=Math.cos(c),$=Math.sin(c),S=l*P-x*$,E=l*$+x*P,L=p,_=Math.cos(z),m=Math.sin(z),N=S,ee=E*_-L*m,te=E*m+L*_;return{x:N,y:ee,z:te}}function lt(l,x,p,c,z){return O(l,x,p,c,z)}function Se(l,x){const p=Number(x).toLocaleString();if(l>=4){const c=Math.floor(Math.random()*3)+1;return n(`heatmap.3d.voxel.joke.${c}`,{value:p})}else return l===3?n("heatmap.3d.voxel.level3",{value:p}):l===2?n("heatmap.3d.voxel.level2",{value:p}):l===1?n("heatmap.3d.voxel.level1",{value:p}):n("heatmap.3d.voxel.level0")}function _e({weeks:l,palette:x="auto",isDark:p=!1,interactive:c=!1,autoRotateInit:z=!1,onResetViewRef:P=null}){const $=Q[x]||(x==="auto"?Q.emerald:null),S=$?p?$.dark:$.light:Array.isArray(x)?x:p?Q.emerald.dark:Q.emerald.light,E=$?p?$.gridColor.dark:$.gridColor.light:p?"rgba(255,255,255,0.06)":"rgba(0,0,0,0.06)",L=-.2,_=.88,[m,N]=i.useState({yaw:L,pitch:_}),[ee,te]=i.useState(z),[he,R]=i.useState(1),j=c?13:10.5,pe=i.useMemo(()=>{const t=[],r=l.length;if(r===0||!c)return[];for(let s=0;s<=7;s++){const o=(s-3.5)*j,d=O(-r/2*j,o,0,m.yaw,m.pitch),h=O(r/2*j,o,0,m.yaw,m.pitch);t.push({d:`M${d.x},${d.y} L${h.x},${h.y}`,key:`horiz-${s}`})}for(let s=0;s<=r;s+=4){const o=(s-r/2)*j,d=O(o,-3.5*j,0,m.yaw,m.pitch),h=O(o,3.5*j,0,m.yaw,m.pitch);t.push({d:`M${d.x},${d.y} L${h.x},${h.y}`,key:`vert-${s}`})}if(r%4!==0){const s=(r-r/2)*j,o=O(s,-3.5*j,0,m.yaw,m.pitch),d=O(s,3.5*j,0,m.yaw,m.pitch);t.push({d:`M${o.x},${o.y} L${d.x},${d.y}`,key:"vert-last"})}return t},[l.length,m,j,c]);i.useEffect(()=>{P&&(P.current={reset:()=>{N({yaw:L,pitch:_}),te(!1),R(1),ye()},toggleAutoRotate:t=>{te(t)}})},[P]);const q=i.useRef(null),F=i.useRef(null),W=i.useRef(!1),ie=i.useRef({x:0,y:0}),Z=i.useRef({yaw:L,pitch:_}),Y=i.useRef({x:0,y:0}),oe=i.useRef({x:0,y:0,time:0}),ne=i.useRef(null),[fe,ge]=i.useState(0),K=i.useRef(null),se=i.useRef(!0);i.useEffect(()=>(se.current=!0,()=>{se.current=!1}),[]);const ye=()=>{K.current&&cancelAnimationFrame(K.current),ge(0);const t=performance.now(),r=1200,s=o=>{if(!se.current)return;const d=o-t,h=Math.min(1,d/r),k=1-Math.pow(1-h,3);ge(k),h<1?K.current=requestAnimationFrame(s):K.current=null};K.current=requestAnimationFrame(s)};i.useEffect(()=>(ye(),()=>{K.current&&cancelAnimationFrame(K.current)}),[c]),i.useEffect(()=>{if(!ee||W.current||typeof window>"u"||typeof window.matchMedia=="function"&&window.matchMedia("(prefers-reduced-motion: reduce)").matches)return;let r;const s=()=>{if(typeof document<"u"&&document.visibilityState==="hidden"){r=requestAnimationFrame(s);return}N(o=>({yaw:o.yaw+.002,pitch:o.pitch})),r=requestAnimationFrame(s)};return r=requestAnimationFrame(s),()=>cancelAnimationFrame(r)},[ee]);const ce=(t,r)=>{c&&(W.current=!0,ie.current={x:t,y:r},Z.current={yaw:m.yaw,pitch:m.pitch},Y.current={x:0,y:0},oe.current={x:t,y:r,time:performance.now()},ne.current&&cancelAnimationFrame(ne.current))},U=(t,r)=>{if(!W.current)return;const s=t-ie.current.x,o=r-ie.current.y,d=performance.now(),h=d-oe.current.time;h>0&&(Y.current={x:(t-oe.current.x)/h,y:(r-oe.current.y)/h}),oe.current={x:t,y:r,time:d};const k=.005,f=Z.current.yaw-s*k,g=Math.PI/2.3,D=Math.max(-g,Math.min(g,Z.current.pitch-o*k));N({yaw:f,pitch:D})},be=()=>{if(!W.current)return;W.current=!1;let t=-Y.current.x*12,r=-Y.current.y*12;const s=.95,o=()=>{W.current||(t*=s,r*=s,!(Math.abs(t)<.01&&Math.abs(r)<.01)&&(N(d=>{const h=d.yaw+t*.005,k=Math.PI/2.3,f=Math.max(-k,Math.min(k,d.pitch+r*.005));return{yaw:h,pitch:f}}),ne.current=requestAnimationFrame(o)))};ne.current=requestAnimationFrame(o)},[b,de]=i.useState(null),[V,xe]=i.useState({x:0,y:0,shiftX:0}),y=i.useRef(null);i.useEffect(()=>()=>{y.current&&clearTimeout(y.current)},[]),i.useEffect(()=>{if(!c||!F.current)return;const t=s=>{s.preventDefault();const o=-s.deltaY*.0025;R(d=>{const h=d+o;return Math.max(.5,Math.min(3,h))})},r=F.current;return r.addEventListener("wheel",t,{passive:!1}),()=>{r.removeEventListener("wheel",t)}},[c]);const C=i.useMemo(()=>{const t=[];return l.forEach((r,s)=>{(Array.isArray(r)?r:[]).forEach((o,d)=>{o&&t.push({key:o.day||`${s}-${d}`,col:s,row:d,level:o.level||0,value:o.value||0,day:o.day,models:o.models||null})})}),t},[l]),ve=j-(c?1.8:1.5),ke=c?38:28,J=t=>Math.max(1.8,Number(t)/4*ke),ae=i.useMemo(()=>{if(C.length===0)return[];const t=l.length;return C.map(r=>{const s=J(r.level),o=Math.sqrt(Math.pow(r.col-t/2,2)+Math.pow(r.row-3.5,2)),d=Math.sqrt(Math.pow(t/2,2)+Math.pow(3.5,2)),h=o/d*.4,k=Math.min(1,Math.max(0,(fe-h)*(1/.6))),f=s*k,g=(r.col-t/2)*j,D=(r.row-3.5)*j,w=ve/2,H=[{x:g-w,y:D-w,z:0},{x:g+w,y:D-w,z:0},{x:g+w,y:D+w,z:0},{x:g-w,y:D+w,z:0},{x:g-w,y:D-w,z:f},{x:g+w,y:D-w,z:f},{x:g+w,y:D+w,z:f},{x:g-w,y:D+w,z:f}].map(B=>O(B.x,B.y,B.z,m.yaw,m.pitch)),me=O(g,D,f/2,m.yaw,m.pitch),Le=[{name:"top",indices:[4,5,6,7],scale:1,normal:[0,0,1]},{name:"bottom",indices:[3,2,1,0],scale:.4,normal:[0,0,-1]},{name:"left",indices:[3,0,4,7],scale:.55,normal:[-1,0,0]},{name:"right",indices:[1,2,6,5],scale:.75,normal:[1,0,0]},{name:"front",indices:[0,1,5,4],scale:.85,normal:[0,-1,0]},{name:"back",indices:[2,3,7,6],scale:.65,normal:[0,1,0]}],Ae=S[Math.min(4,Math.max(0,Number(r.level)||0))],Ne=[],Re=.35,De=-.4,Pe=.83;return Le.forEach(B=>{const we=lt(B.normal[0],B.normal[1],B.normal[2],m.yaw,m.pitch);if(we.z>.001){const Ce=H[B.indices[0]],ze=H[B.indices[1]],Me=H[B.indices[2]],Te=H[B.indices[3]],Ye=`M${Ce.x},${Ce.y} L${ze.x},${ze.y} L${Me.x},${Me.y} L${Te.x},${Te.y} Z`,Ue=we.x*Re+we.y*De+we.z*Pe,Xe=p?.18:0,Be=B.scale*(.82+.28*Math.max(0,Ue))+Xe;Ne.push({name:B.name,d:Ye,fill:st(Ae,Be)})}}),{...r,centerProj:me,renderedFaces:Ne}})},[C,m,S,l.length,fe,j,ve,ke]),le=i.useMemo(()=>[...ae].sort((t,r)=>t.centerProj.z-r.centerProj.z),[ae]),M=i.useMemo(()=>{if(le.length===0)return{minX:-100,minY:-100,maxX:100,maxY:100};let t=1/0,r=1/0,s=-1/0,o=-1/0;return le.forEach(d=>{const h=j*2,k=d.centerProj.x,f=d.centerProj.y;k-h<t&&(t=k-h),k+h>s&&(s=k+h),f-h<r&&(r=f-h),f+h>o&&(o=f+h)}),{minX:t,minY:r,maxX:s,maxY:o}},[le,j]),A=12,a=M.maxX-M.minX+A*2,u=M.maxY-M.minY+A*2,T=a/he,v=u/he,X=M.minX-A+(a-T)/2,I=M.minY-A+(u-v)/2,G=`${X} ${I} ${T} ${v}`;return C.length===0?e.jsx("div",{className:"py-8 text-center text-sm text-oai-gray-500",children:n("heatmap.empty")}):e.jsxs("div",{ref:F,className:`relative select-none outline-none ${c?"cursor-grab active:cursor-grabbing w-full h-full flex items-center justify-center":"w-full overflow-hidden flex justify-center"}`,onMouseDown:t=>{if(!c)return;ce(t.clientX,t.clientY);const r=o=>U(o.clientX,o.clientY),s=()=>{be(),window.removeEventListener("mousemove",r),window.removeEventListener("mouseup",s)};window.addEventListener("mousemove",r),window.addEventListener("mouseup",s)},onTouchStart:t=>{!c||t.touches.length===0||ce(t.touches[0].clientX,t.touches[0].clientY)},onTouchMove:t=>{!c||t.touches.length===0||U(t.touches[0].clientX,t.touches[0].clientY)},onTouchEnd:()=>{c&&be()},children:[e.jsxs("svg",{ref:q,viewBox:G,width:c?"95%":"100%",height:c?"95%":"auto",role:"img","aria-label":n("heatmap.iso.aria")||"3D interactive activity heatmap",style:{display:"block",width:"100%",height:"auto",maxWidth:c?"none":`${a}px`,maxHeight:c?"78vh":"none"},className:"transition-transform duration-300 ease-out",children:[c&&pe.map(t=>e.jsx("path",{d:t.d,fill:"none",stroke:E,strokeWidth:.25,strokeDasharray:"1.5 2.5",strokeLinecap:"round"},t.key)),le.map(t=>{const r=b&&b.key===t.key;return e.jsxs("g",{onMouseEnter:s=>{if(y.current&&(clearTimeout(y.current),y.current=null),de(t),c&&q.current){const o=O((t.col-l.length/2)*j,(t.row-3.5)*j,J(t.level),m.yaw,m.pitch);let d=0,h=0;const k=q.current;if(F.current&&typeof k.createSVGPoint=="function"&&typeof k.getScreenCTM=="function")try{const w=k.createSVGPoint();w.x=o.x,w.y=o.y;const re=k.getScreenCTM();if(re){const H=w.matrixTransform(re),me=F.current.getBoundingClientRect();d=H.x-me.left,h=H.y-me.top}}catch{const re=k.getBoundingClientRect(),H=M.maxX-M.minX+A*2,me=M.maxY-M.minY+A*2;d=(o.x-(M.minX-A))/H*re.width,h=(o.y-(M.minY-A))/me*re.height}else{const w=k.getBoundingClientRect(),re=M.maxX-M.minX+A*2,H=M.maxY-M.minY+A*2;d=(o.x-(M.minX-A))/re*w.width,h=(o.y-(M.minY-A))/H*w.height}const f=140;let g=0;const D=F.current?F.current.getBoundingClientRect().width:k.getBoundingClientRect().width;d<f?g=f-d:d>D-f&&(g=D-f-d),xe({x:d,y:h,shiftX:g})}},onMouseLeave:()=>{y.current&&clearTimeout(y.current),y.current=setTimeout(()=>{de(null)},150)},className:"transition-all duration-200",style:{filter:r?"brightness(1.15) drop-shadow(0 4px 6px rgba(0,0,0,0.15))":"none",cursor:c?"pointer":"default"},children:[!c&&t.day&&e.jsx("title",{children:`${t.day}: ${Number(t.value).toLocaleString()} tokens`}),t.renderedFaces.map((s,o)=>e.jsx("path",{d:s.d,fill:s.fill,stroke:s.fill,strokeWidth:.25,strokeLinejoin:"round"},o))]},t.key)})]}),c&&b&&e.jsxs("div",{onMouseEnter:()=>{y.current&&(clearTimeout(y.current),y.current=null)},onMouseLeave:()=>{y.current&&clearTimeout(y.current),y.current=setTimeout(()=>{de(null)},150)},className:"absolute z-[9999] w-0 h-0 transition-all duration-100 ease-out",style:{left:`${V.x}px`,top:`${V.y}px`},children:[e.jsxs("div",{className:"absolute left-0 bottom-[10px] backdrop-blur-md bg-white/90 dark:bg-oai-gray-900/90 border border-oai-gray-200/50 dark:border-oai-gray-800/50 shadow-xl rounded-xl p-3.5 max-w-[280px] min-w-[200px] flex flex-col gap-2 animate-in fade-in zoom-in-95 duration-100",style:{transform:`translateX(calc(-50% + ${V.shiftX}px))`},children:[e.jsxs("div",{className:"flex items-center justify-between border-b border-oai-gray-100 dark:border-oai-gray-800/80 pb-1.5",children:[e.jsx("span",{className:"text-[11px] font-semibold text-oai-gray-500 dark:text-oai-gray-400",children:b.day}),(()=>{const t=b.level===0?p?"#9ca3af":"#6b7280":S[b.level];return e.jsxs("span",{className:"text-[10px] px-2 py-0.5 rounded-full font-medium",style:{backgroundColor:t+"22",color:t,border:`1px solid ${t}44`},children:["Level ",b.level]})})()]}),e.jsxs("div",{className:"flex flex-col gap-2",children:[e.jsxs("div",{className:"flex items-baseline gap-1",children:[e.jsx("span",{className:"text-lg font-bold text-oai-gray-900 dark:text-white leading-none",children:Number(b.value).toLocaleString()}),e.jsx("span",{className:"text-[10px] text-oai-gray-400 uppercase tracking-wider font-semibold",children:"Tokens"})]}),b.models&&Object.keys(b.models).length>0?e.jsxs("div",{className:"mt-1.5 border-t border-oai-gray-100 dark:border-oai-gray-800/60 pt-2 flex flex-col gap-1.5",children:[e.jsx("div",{className:"text-[10px] font-semibold text-oai-gray-400 dark:text-oai-gray-500 uppercase tracking-wider",children:"Model Breakdown"}),e.jsx("div",{className:"flex flex-col gap-2 max-h-[150px] overflow-y-auto pr-1.5 scrollbar-thin",children:Object.entries(b.models).map(([t,r])=>({name:t,val:Number(r)})).sort((t,r)=>r.val-t.val).map(({name:t,val:r})=>{const s=Number(b.value)||1,o=Math.round(r/s*100);return e.jsxs("div",{className:"flex flex-col gap-1",children:[e.jsxs("div",{className:"flex items-center justify-between text-[11px] gap-3",children:[e.jsx("span",{className:"font-medium text-oai-gray-750 dark:text-oai-gray-200 truncate max-w-[120px]",title:t,children:t}),e.jsxs("div",{className:"flex items-center gap-1.5 shrink-0",children:[e.jsx("span",{className:"font-mono text-oai-gray-900 dark:text-oai-gray-100 font-semibold",children:r.toLocaleString()}),e.jsxs("span",{className:"text-[9px] text-oai-gray-450 dark:text-oai-gray-500 min-w-[28px] text-right font-medium",children:[o,"%"]})]})]}),e.jsx("div",{className:"w-full h-1 bg-oai-gray-100 dark:bg-oai-gray-800/85 rounded-full overflow-hidden",children:e.jsx("div",{className:"h-full rounded-full transition-all duration-300",style:{width:`${o}%`,backgroundColor:S[4],boxShadow:`0 0 4px ${S[4]}55`}})})]},t)})})]}):e.jsx("p",{className:"text-[11px] text-oai-gray-600 dark:text-oai-gray-300 leading-relaxed font-normal mt-1 border-t border-dashed border-oai-gray-100 dark:border-oai-gray-800/60 pt-1.5",children:Se(b.level,b.value)})]})]}),e.jsx("div",{className:"absolute bottom-[6px] left-0 -translate-x-1/2 w-2.5 h-2.5 rotate-45 bg-white dark:bg-oai-gray-900 border-r border-b border-oai-gray-200/50 dark:border-oai-gray-800/50 shadow-sm",style:{marginBottom:"1px"}})]})]})}const it=12,ct=3,dt=26,mt=["#ebedf0","#f7d08a","#f2b757","#eca02f","#d9861a"],xt=["#30363d","#7a4711","#bf7015","#eca02f","#f7c877"];function $e(l){const x=Number(l);return Number.isFinite(x)?x<1e3?x.toLocaleString():Oe(x,{decimals:2}):"0"}function ut(l){if(typeof l!="string")return null;const x=/^(\d{4})-(\d{2})-(\d{2})$/.exec(l.trim());if(!x)return null;const p=new Date(Date.UTC(Number(x[1]),Number(x[2])-1,Number(x[3])));return Number.isFinite(p.getTime())?p:null}function Ee(l,x){return new Date(Date.UTC(l.getUTCFullYear(),l.getUTCMonth(),l.getUTCDate()+x))}function ht(l,x){return Math.floor((Date.UTC(x.getUTCFullYear(),x.getUTCMonth(),x.getUTCDate())-Date.UTC(l.getUTCFullYear(),l.getUTCMonth(),l.getUTCDate()))/864e5)}function pt(l,x){const p=x==="mon"?1:0,c=l.getUTCDay();return Ee(l,-((c-p+7)%7))}function ft(l,x,p,c){if(!l)return[];const z=ut(x)||new Date,P=[];for(let _=11;_>=0;_-=1)P.push(new Date(Date.UTC(z.getUTCFullYear(),z.getUTCMonth()-_,1)));const $=pt(z,p),S=Ee($,-(l-1)*7),E=[],L=new Set;for(const _ of P){const m=Math.floor(ht(S,_)/7);m<0||m>=l||L.has(m)||(L.add(m),E.push({label:c[_.getUTCMonth()],index:m}))}return E}function vt({heatmap:l,timeZoneLabel:x,timeZoneShortLabel:p,hideLegend:c=!1,embedded:z=!1}){const{resolvedTheme:P}=We(),{currency:$,rate:S}=qe(),E=P==="dark",L=E?xt:mt,_=i.useRef(null),m=i.useRef(null),[N,ee]=i.useState(null),[te,he]=i.useState({x:0,y:0,shiftX:0}),R=i.useRef(null);i.useEffect(()=>()=>{R.current&&clearTimeout(R.current)},[]);const[j,pe]=i.useState(!1),[q,F]=i.useState(!1),[W,ie]=i.useState(!1),Z=i.useRef(null),[Y,oe]=i.useState("amber"),ne=[n("heatmap.month.jan"),n("heatmap.month.feb"),n("heatmap.month.mar"),n("heatmap.month.apr"),n("heatmap.month.may"),n("heatmap.month.jun"),n("heatmap.month.jul"),n("heatmap.month.aug"),n("heatmap.month.sep"),n("heatmap.month.oct"),n("heatmap.month.nov"),n("heatmap.month.dec")],fe=(a,u)=>{if(!u||!u.day)return;R.current&&(clearTimeout(R.current),R.current=null),ee(u);const T=a.currentTarget.getBoundingClientRect(),v=typeof window<"u"?window.innerWidth:1024,X=T.left+T.width/2,I=T.top,G=140;let t=0;X<G?t=G-X:X>v-G&&(t=v-G-X),he({x:X,y:I,shiftX:t})},ge=()=>{R.current&&clearTimeout(R.current),R.current=setTimeout(()=>{ee(null)},150)},K=()=>{F(!1),pe(!0)},se=()=>{F(!0)},ye=a=>{a.target===a.currentTarget&&q&&(pe(!1),F(!1))};i.useEffect(()=>{if(!j||q)return;const a=u=>{u.key==="Escape"&&se()};return window.addEventListener("keydown",a),()=>{window.removeEventListener("keydown",a)}},[j,q]);const ce=i.useMemo(()=>({emerald:{accentText:"text-emerald-500 dark:text-emerald-400",accentBg:"bg-emerald-500/10 dark:bg-emerald-400/10",accentBorder:"border-emerald-500/20 dark:border-emerald-400/15",hoverBorder:"hover:border-emerald-500/30 dark:hover:border-emerald-400/30",hoverGlow:"hover:shadow-[0_0_20px_-3px_rgba(16,185,129,0.15)] hover:dark:shadow-[0_0_20px_-3px_rgba(52,211,153,0.25)]",rawColor:"#10b981"},ocean:{accentText:"text-blue-500 dark:text-blue-400",accentBg:"bg-blue-500/10 dark:bg-blue-400/10",accentBorder:"border-blue-500/20 dark:border-blue-400/15",hoverBorder:"hover:border-blue-500/30 dark:hover:border-blue-400/30",hoverGlow:"hover:shadow-[0_0_20px_-3px_rgba(59,130,246,0.15)] hover:dark:shadow-[0_0_20px_-3px_rgba(96,165,250,0.25)]",rawColor:"#3b82f6"},neon:{accentText:"text-purple-500 dark:text-purple-400",accentBg:"bg-purple-500/10 dark:bg-purple-400/10",accentBorder:"border-purple-500/20 dark:border-purple-400/15",hoverBorder:"hover:border-purple-500/30 dark:hover:border-purple-400/30",hoverGlow:"hover:shadow-[0_0_20px_-3px_rgba(168,85,247,0.15)] hover:dark:shadow-[0_0_20px_-3px_rgba(192,132,252,0.25)]",rawColor:"#a855f7"},amber:{accentText:"text-amber-500 dark:text-amber-400",accentBg:"bg-amber-500/10 dark:bg-amber-400/10",accentBorder:"border-amber-500/20 dark:border-amber-400/15",hoverBorder:"hover:border-amber-500/30 dark:hover:border-amber-400/30",hoverGlow:"hover:shadow-[0_0_20px_-3px_rgba(245,158,11,0.15)] hover:dark:shadow-[0_0_20px_-3px_rgba(245,158,11,0.25)]",rawColor:"#f59e0b"}}),[]),U=ce[Y]||ce.emerald,be=Q[Y]?E?Q[Y].dark:Q[Y].light:E?Q.emerald.dark:Q.emerald.light,[b,de]=i.useState(()=>{if(z)return"2d";try{return window.localStorage?.getItem("tt:heatmap-view")==="3d"?"3d":"2d"}catch{return"2d"}});i.useEffect(()=>{if(!z)try{window.localStorage?.setItem("tt:heatmap-view",b)}catch{}},[b,z]),i.useEffect(()=>{if(b!=="2d")return;const a=_.current;a&&(a.scrollLeft=a.scrollWidth)},[l?.weeks,b]),i.useEffect(()=>{const a=_.current;if(!a)return;const u=()=>{R.current&&(clearTimeout(R.current),R.current=null),ee(null)};return a.addEventListener("scroll",u),()=>a.removeEventListener("scroll",u)},[]);const V=l?.week_starts_on==="mon"?"mon":"sun",xe=i.useMemo(()=>{const a=Array.isArray(l?.weeks)?l.weeks:[];if(!a.length)return{weeks:[]};const u=[];for(const T of a)for(const v of Array.isArray(T)?T:[])v?.day&&u.push({day:v.day,total_tokens:v.total_tokens??v.value??0,billable_total_tokens:v.billable_total_tokens??v.value??v.total_tokens??0,models:v.models??null});return Fe({dailyRows:u,weeks:Math.max(52,a.length),to:l?.to,weekStartsOn:V})},[l?.to,l?.weeks,V]),y=xe?.weeks||[],C=i.useMemo(()=>{let a=0,u=0,T={day:"无数据",value:0},v=0,X=0;const I=[];y.forEach(f=>{(Array.isArray(f)?f:[]).forEach(g=>{g&&g.day&&I.push(g)})}),I.sort((f,g)=>f.day.localeCompare(g.day)),I.forEach(f=>{const g=Number(f.value)||0;a+=g,g>0?(u++,v++,v>X&&(X=v)):v=0,g>T.value&&(T={day:f.day,value:g})});const G=I.length||365,t=(u/G*100).toFixed(1);let r="heatmap.3d.modal.ai.eval.default",s="heatmap.3d.modal.ai.title.default";a>=15e6?(s="heatmap.3d.modal.ai.title.peak",r="heatmap.3d.modal.ai.eval.peak"):a>=5e6?(s="heatmap.3d.modal.ai.title.heavy",r="heatmap.3d.modal.ai.eval.heavy"):a>=1e6?(s="heatmap.3d.modal.ai.title.core",r="heatmap.3d.modal.ai.eval.core"):a>=2e4&&(s="heatmap.3d.modal.ai.title.steady",r="heatmap.3d.modal.ai.eval.steady");const o=l?.total_cost_usd,d=Number(o),k=o!=null&&(typeof o!="string"||o.trim()!=="")&&Number.isFinite(d)?d:a/15e5;return{totalTokens:a,activeDays:u,activeRate:t,maxSingleDay:T,maxStreak:X,aiEvaluationTitleKey:s,aiEvaluationKey:r,totalCostUsd:k}},[y,l?.total_cost_usd]),je=i.useMemo(()=>He(C.totalCostUsd,{currency:$,rate:S}),[C.totalCostUsd,$,S]),ve=V==="mon"?["Mon","Tue","Wed","Thu","Fri","Sat","Sun"].map(a=>n(`heatmap.day.${a.toLowerCase()}`)):["Sun","Mon","Tue","Wed","Thu","Fri","Sat"].map(a=>n(`heatmap.day.${a.toLowerCase()}`)),ke=i.useMemo(()=>ft(y.length,xe?.to,V,ne),[xe?.to,y.length,V,ne]);if(!y.length)return e.jsx("div",{className:"py-8 text-center text-sm text-oai-gray-500",children:n("heatmap.empty")});const J=z?10:it,ae=z?2:ct,le=z?22:dt,M=le+y.length*J+Math.max(0,y.length-1)*ae,A=`${le}px repeat(${y.length}, ${J}px)`;return e.jsxs("div",{ref:m,className:z?"relative":"relative rounded-xl border border-oai-gray-200 dark:border-oai-gray-800 bg-white dark:bg-oai-gray-900 p-5",children:[!z&&e.jsxs("div",{className:"flex items-baseline justify-between mb-3",children:[e.jsx("h3",{className:"text-sm font-medium text-oai-gray-500 dark:text-oai-gray-300 uppercase tracking-wide",children:n("heatmap.title")}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsxs("div",{role:"tablist","aria-label":"Heatmap view",className:"flex rounded-md border border-oai-gray-200 dark:border-oai-gray-800 p-0.5 text-[10px]",children:[e.jsx("button",{type:"button",role:"tab","aria-selected":b==="2d",onClick:()=>de("2d"),className:b==="2d"?"px-2 py-0.5 rounded bg-oai-gray-100 text-oai-black dark:bg-oai-gray-800 dark:text-oai-white font-medium":"px-2 py-0.5 rounded text-oai-gray-500 dark:text-oai-gray-400 hover:text-oai-gray-700 dark:hover:text-oai-gray-200",children:n("heatmap.view.2d")}),e.jsx("button",{type:"button",role:"tab","aria-selected":b==="3d",onClick:()=>de("3d"),className:b==="3d"?"px-2 py-0.5 rounded bg-oai-gray-100 text-oai-black dark:bg-oai-gray-800 dark:text-oai-white font-medium":"px-2 py-0.5 rounded text-oai-gray-500 dark:text-oai-gray-400 hover:text-oai-gray-700 dark:hover:text-oai-gray-200",children:n("heatmap.view.3d")})]}),e.jsx("span",{className:"text-xs text-oai-gray-400 dark:text-oai-gray-450",children:p||n("heatmap.legend.utc")})]})]}),b==="3d"&&e.jsxs("div",{onClick:K,className:"cursor-pointer group relative overflow-hidden rounded-lg hover:border-oai-gray-350 dark:hover:border-oai-gray-700 border border-transparent transition-all",title:n("heatmap.3d.hover_tip"),children:[e.jsx(_e,{weeks:y,isDark:E,palette:Y}),e.jsx("div",{className:"absolute inset-0 bg-gradient-to-t from-oai-gray-900/5 to-transparent pointer-events-none opacity-0 group-hover:opacity-100 transition-opacity flex items-end justify-center pb-2",children:e.jsxs("span",{className:"text-[10px] bg-white/95 dark:bg-oai-gray-900/95 shadow border border-oai-gray-200/60 dark:border-oai-gray-800/80 px-2.5 py-1 rounded-full font-medium text-oai-gray-500 dark:text-oai-gray-400 flex items-center gap-1 transform translate-y-2 group-hover:translate-y-0 transition-transform duration-200",children:[e.jsx(Ve,{size:9}),n("heatmap.3d.hover_tip")]})})]}),b==="2d"&&e.jsx("div",{ref:_,className:"overflow-x-auto overflow-y-hidden heatmap-scroll-thin",children:e.jsxs("div",{style:{minWidth:M},children:[e.jsxs("div",{className:"grid text-[10px] uppercase text-oai-gray-400 dark:text-oai-gray-400 mb-1",style:{gridTemplateColumns:A,columnGap:ae},children:[e.jsx("span",{}),ke.map(a=>e.jsx("span",{style:{gridColumnStart:a.index+2},className:"whitespace-nowrap",children:a.label},`${a.label}-${a.index}`))]}),e.jsxs("div",{className:"grid",style:{gridTemplateColumns:A,columnGap:ae},children:[e.jsx("div",{className:"grid text-[10px] text-oai-gray-400 dark:text-oai-gray-400 sticky left-0 bg-white dark:bg-oai-gray-900 pr-2",style:{gridTemplateRows:`repeat(7, ${J}px)`,rowGap:ae},children:ve.map(a=>e.jsx("span",{className:"leading-none",children:a},a))}),e.jsx("div",{className:"grid",style:{gridAutoFlow:"column",gridTemplateRows:`repeat(7, ${J}px)`,gap:ae},children:y.map((a,u)=>(Array.isArray(a)?a:[]).map((T,v)=>{if(!T)return null;const X=T.day||`e-${u}-${v}`,I=Number(T.level)||0,G=L[I]||L[0];return e.jsx("span",{onMouseEnter:t=>fe(t,T),onMouseLeave:ge,className:"rounded-[2px] transition-transform hover:scale-125 hover:z-10 cursor-pointer",style:{width:J,height:J,background:G}},X)}))})]})]})}),!c&&e.jsxs("div",{className:"flex items-center justify-center gap-2 mt-3",children:[e.jsx("span",{className:"text-[10px] text-oai-gray-400 dark:text-oai-gray-400",children:n("heatmap.legend.less")}),e.jsx("div",{className:"flex gap-0.5",children:L.map((a,u)=>e.jsx("span",{className:"rounded-[1px]",style:{width:10,height:10,background:a}},u))}),e.jsx("span",{className:"text-[10px] text-oai-gray-400 dark:text-oai-gray-400",children:n("heatmap.legend.more")})]}),j&&e.jsx("div",{onAnimationEnd:ye,onClick:a=>{a.target===a.currentTarget&&se()},className:`fixed inset-0 z-50 flex items-center justify-center p-3 md:p-6 backdrop-blur-md bg-black/15 dark:bg-black/40 ${q?"animate-tt-fade-out":"animate-tt-fade-in"}`,children:e.jsxs("div",{className:`relative w-full max-w-6xl h-[88vh] backdrop-blur-2xl bg-white/90 dark:bg-oai-gray-900/90 border border-oai-gray-200/50 dark:border-white/10 shadow-2xl rounded-2xl flex flex-col md:flex-row overflow-hidden ${q?"animate-tt-modal-exit":"animate-tt-modal"}`,children:[e.jsx("button",{type:"button",onClick:se,className:"absolute top-4 right-4 z-50 p-2 rounded-full border border-oai-gray-200/60 dark:border-oai-gray-800/60 bg-white/50 dark:bg-oai-gray-900/50 text-oai-gray-500 dark:text-oai-gray-400 hover:text-oai-gray-900 dark:hover:text-white hover:rotate-90 hover:scale-105 active:scale-95 transition-all duration-300",children:e.jsx(Ie,{size:16})}),e.jsxs("div",{className:"w-full md:w-[340px] border-b md:border-b-0 md:border-r border-zinc-200/50 dark:border-zinc-800/40 p-5 md:p-6 flex flex-col gap-6 overflow-y-auto backdrop-blur-md bg-zinc-50/50 dark:bg-zinc-950/50",children:[e.jsxs("div",{children:[e.jsxs("div",{className:"flex items-center gap-1.5 select-none",children:[e.jsxs("span",{className:"relative flex h-1.5 w-1.5",children:[e.jsx("span",{className:"animate-ping absolute inline-flex h-full w-full rounded-full opacity-75",style:{backgroundColor:U.rawColor}}),e.jsx("span",{className:"relative inline-flex rounded-full h-1.5 w-1.5",style:{backgroundColor:U.rawColor}})]}),e.jsx("span",{className:"text-[9px] font-extrabold uppercase tracking-widest font-mono text-zinc-400 dark:text-zinc-500",children:"3D Insight"})]}),e.jsx("h4",{className:"text-xl font-black text-zinc-900 dark:text-zinc-50 tracking-tight leading-none mt-2 select-none",children:n("heatmap.3d.modal.title")}),e.jsx("p",{className:"text-[11px] leading-relaxed text-zinc-400 dark:text-zinc-500 mt-2 font-normal select-none",children:n("heatmap.3d.modal.desc")})]}),e.jsxs("div",{className:"grid grid-cols-2 gap-x-5 gap-y-5 border-y border-zinc-200/50 dark:border-zinc-800/50 py-5 select-none",children:[e.jsxs("div",{className:"flex flex-col gap-1 relative group cursor-help",children:[e.jsx("div",{className:"absolute left-0 bottom-full mb-2 pointer-events-none opacity-0 translate-y-1 group-hover:opacity-100 group-hover:translate-y-0 transition-all duration-200 z-50",children:e.jsxs("div",{className:"bg-white dark:bg-zinc-950 text-zinc-900 dark:text-zinc-50 text-[10px] font-semibold font-mono rounded-lg px-2.5 py-1.5 shadow-xl border border-zinc-200 dark:border-zinc-800/80 whitespace-nowrap flex flex-col",children:[e.jsx("span",{className:"text-[9px] text-zinc-400 dark:text-zinc-500",children:n("heatmap.3d.modal.stats.precision_total_tokens")}),e.jsxs("span",{className:"mt-0.5 font-bold text-zinc-900 dark:text-zinc-50",children:[C.totalTokens.toLocaleString()," Tokens"]})]})}),e.jsx("span",{className:"text-[9px] font-bold text-zinc-400 dark:text-zinc-500 uppercase tracking-widest font-mono",children:n("heatmap.3d.modal.stats.total_tokens")}),e.jsxs("div",{className:"flex items-baseline gap-1.5",children:[e.jsx("span",{className:"text-xl font-black text-zinc-900 dark:text-zinc-50 tracking-tight font-mono transition-transform duration-200 group-hover:-translate-y-[1px]",children:$e(C.totalTokens)}),e.jsx("div",{className:"opacity-30 group-hover:opacity-60 transition-opacity",children:e.jsx("svg",{width:"24",height:"10",viewBox:"0 0 24 10",fill:"none",children:e.jsx("path",{d:"M1 9C3 7 5 7 7 4C9 1 11 0 13 2C15 4 17 0 23 0",stroke:U.rawColor,strokeWidth:"1.2",strokeLinecap:"round",strokeLinejoin:"round"})})})]})]}),e.jsxs("div",{className:"flex flex-col gap-1 group cursor-default",children:[e.jsx("span",{className:"text-[9px] font-bold text-zinc-400 dark:text-zinc-500 uppercase tracking-widest font-mono",children:n("heatmap.3d.modal.stats.estimated_cost")}),e.jsx("span",{className:"text-xl font-black text-zinc-900 dark:text-zinc-50 tracking-tight font-mono transition-transform duration-200 group-hover:-translate-y-[1px]",children:je})]}),e.jsxs("div",{className:"flex flex-col gap-1 group cursor-default",children:[e.jsx("span",{className:"text-[9px] font-bold text-zinc-400 dark:text-zinc-500 uppercase tracking-widest font-mono",children:n("heatmap.3d.modal.stats.active_rate_days")}),e.jsxs("span",{className:"text-xl font-black text-zinc-900 dark:text-zinc-50 tracking-tight font-mono transition-transform duration-200 group-hover:-translate-y-[1px]",children:[C.activeRate,"% ",e.jsxs("span",{className:"text-[10px] font-bold text-zinc-400 dark:text-zinc-500 font-mono",children:["(",C.activeDays,"D)"]})]})]}),e.jsxs("div",{className:"flex flex-col gap-1 group cursor-default",children:[e.jsx("span",{className:"text-[9px] font-bold text-zinc-400 dark:text-zinc-500 uppercase tracking-widest font-mono",children:n("heatmap.3d.modal.stats.max_streak")}),e.jsxs("span",{className:"text-xl font-black text-amber-500 tracking-tight font-mono transition-transform duration-200 group-hover:-translate-y-[1px]",children:[C.maxStreak," ",e.jsx("span",{className:"text-[10px] font-bold text-amber-500/80 font-mono",children:n("heatmap.3d.modal.stats.days_suffix")})]})]}),e.jsxs("div",{className:"flex flex-col gap-1 col-span-2 relative group cursor-help",children:[e.jsx("div",{className:"absolute left-0 bottom-full mb-2 pointer-events-none opacity-0 translate-y-1 group-hover:opacity-100 group-hover:translate-y-0 transition-all duration-200 z-50",children:e.jsxs("div",{className:"bg-white dark:bg-zinc-950 text-zinc-900 dark:text-zinc-50 text-[10px] font-semibold font-mono rounded-lg px-2.5 py-1.5 shadow-xl border border-zinc-200 dark:border-zinc-800/80 whitespace-nowrap flex flex-col",children:[e.jsx("span",{className:"text-[9px] text-zinc-400 dark:text-zinc-500",children:n("heatmap.3d.modal.stats.precision_peak_value")}),e.jsxs("span",{className:"mt-0.5 font-bold text-zinc-900 dark:text-zinc-50",children:[C.maxSingleDay.value>0?C.maxSingleDay.value.toLocaleString():n("heatmap.3d.modal.stats.no_data")," Tokens"]}),e.jsx("span",{className:"text-[8px] text-zinc-400 dark:text-zinc-500 mt-0.5",children:C.maxSingleDay.day!=="无数据"?C.maxSingleDay.day:n("heatmap.3d.modal.stats.no_data")})]})}),e.jsx("span",{className:"text-[9px] font-bold text-zinc-400 dark:text-zinc-500 uppercase tracking-widest font-mono",children:n("heatmap.3d.modal.stats.peak_day")}),e.jsxs("span",{className:"text-xl font-black text-zinc-900 dark:text-zinc-50 tracking-tight font-mono transition-transform duration-200 group-hover:-translate-y-[1px]",children:[C.maxSingleDay.value>0?$e(C.maxSingleDay.value):n("heatmap.3d.modal.stats.no_data")," ",e.jsxs("span",{className:"text-[10px] font-bold text-zinc-400 dark:text-zinc-500 font-mono",children:["(",C.maxSingleDay.day!=="无数据"?C.maxSingleDay.day:n("heatmap.3d.modal.stats.no_data"),")"]})]})]})]}),e.jsxs("div",{className:"flex flex-col gap-2.5 py-1",children:[e.jsxs("div",{className:"flex items-center gap-1.5 select-none",children:[e.jsx(nt,{size:11,style:{color:U.rawColor}}),e.jsx("span",{className:"text-[9px] font-extrabold uppercase tracking-widest font-mono",style:{color:U.rawColor},children:n(C.aiEvaluationTitleKey)})]}),e.jsxs("div",{className:"pl-3.5 border-l-2 relative transition-all duration-300 group",style:{borderColor:U.rawColor},children:[e.jsx("div",{className:"absolute inset-y-0 left-0 w-[3px] blur-[2px] opacity-15 pointer-events-none rounded-full",style:{backgroundColor:U.rawColor}}),e.jsx("p",{className:"text-[11px] leading-relaxed text-zinc-600 dark:text-zinc-400 font-normal",children:n(C.aiEvaluationKey)})]})]}),e.jsx("div",{className:"mt-auto border-t border-zinc-200/50 dark:border-zinc-800/50 pt-4 select-none",children:e.jsxs("div",{className:"flex flex-col gap-2",children:[e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx("span",{className:"text-[9px] font-bold text-zinc-400 dark:text-zinc-500 uppercase tracking-widest font-mono",children:n("heatmap.3d.modal.legend.title")}),e.jsx("span",{className:"text-[10px] font-medium text-zinc-500 dark:text-zinc-400",children:n(`heatmap.3d.modal.palette.${Y}`)})]}),e.jsx("div",{className:"flex gap-1",children:be.map((a,u)=>e.jsx("div",{className:"flex-1 h-1 rounded-[2px]",style:{backgroundColor:a},title:`Level ${u}`},u))})]})})]}),e.jsxs("div",{className:"flex-1 h-full relative flex items-center justify-center overflow-hidden p-4",children:[e.jsx("div",{className:"absolute top-1/4 left-1/3 w-96 h-96 rounded-full blur-[130px] pointer-events-none -translate-x-1/2 -translate-y-1/2 transition-all duration-500",style:{backgroundColor:U.rawColor+"15"}}),e.jsx("div",{className:"absolute bottom-1/4 right-1/3 w-80 h-80 rounded-full blur-[120px] pointer-events-none translate-x-1/2 translate-y-1/2 bg-purple-500/[0.04] dark:bg-purple-500/[0.08]"}),e.jsxs("div",{className:"absolute top-4 left-1/2 -translate-x-1/2 flex items-center gap-3 p-1.5 backdrop-blur-md bg-white/70 dark:bg-oai-gray-900/75 border border-oai-gray-200/60 dark:border-oai-gray-800/80 rounded-full shadow-lg z-30 select-none",children:[e.jsx("div",{className:"flex items-center gap-1.5 px-2",children:Object.keys(ce).map(a=>{const u=Y===a;return e.jsx("button",{type:"button",onClick:()=>oe(a),title:n(`heatmap.3d.modal.palette.${a}`),className:`w-3.5 h-3.5 rounded-full transition-all duration-200 relative hover:scale-125 ${a==="emerald"?"bg-[#10b981]":a==="ocean"?"bg-[#3b82f6]":a==="neon"?"bg-[#a855f7]":"bg-[#f59e0b]"}`,children:u&&e.jsx("span",{className:"absolute inset-0 rounded-full ring-2 ring-offset-1 ring-offset-white dark:ring-offset-oai-gray-900 ring-oai-gray-900 dark:ring-white scale-110"})},a)})}),e.jsx("div",{className:"w-[1px] h-4 bg-oai-gray-200 dark:bg-oai-gray-800"}),e.jsxs("div",{className:"flex items-center gap-1 pr-1",children:[e.jsx("button",{type:"button",onClick:()=>{const a=!W;ie(a),Z.current&&Z.current.toggleAutoRotate(a)},title:W?n("heatmap.3d.modal.control.pause"):n("heatmap.3d.modal.control.play"),className:`p-1.5 rounded-full transition-all duration-200 hover:bg-oai-gray-100 dark:hover:bg-oai-gray-800 ${W?U.accentText:"text-oai-gray-400 hover:text-oai-gray-600 dark:hover:text-oai-gray-250"}`,children:W?e.jsx(Qe,{size:12}):e.jsx(tt,{size:12})}),e.jsx("button",{type:"button",onClick:()=>{ie(!1),Z.current&&Z.current.reset()},title:n("heatmap.3d.modal.control.reset"),className:"p-1.5 rounded-full text-oai-gray-400 hover:text-oai-gray-600 dark:hover:text-oai-gray-250 hover:bg-oai-gray-100 dark:hover:bg-oai-gray-800 transition-all duration-200",children:e.jsx(rt,{size:12})})]})]}),e.jsx(_e,{weeks:y,isDark:E,interactive:!0,palette:Y,autoRotateInit:W,onResetViewRef:Z}),e.jsxs("div",{className:"absolute bottom-4 right-4 flex items-center gap-1.5 text-[9px] font-bold text-oai-gray-400 bg-white/80 dark:bg-oai-gray-900/80 border border-oai-gray-200/50 dark:border-oai-gray-800/80 rounded-md px-2.5 py-1.5 select-none pointer-events-none backdrop-blur-md shadow-sm",children:[e.jsx(Ze,{size:10,className:U.accentText}),e.jsx("span",{children:n("heatmap.3d.modal.footer.tip")})]})]})]})}),N&&!j&&typeof document<"u"&&Ge.createPortal(e.jsxs("div",{className:"fixed z-[9999] w-0 h-0 transition-all duration-100 ease-out pointer-events-none",style:{left:`${te.x}px`,top:`${te.y}px`},children:[e.jsxs("div",{className:"absolute left-0 bottom-[10px] backdrop-blur-md bg-white/95 dark:bg-oai-gray-900/95 border border-oai-gray-200/50 dark:border-oai-gray-800/50 shadow-xl rounded-xl p-3.5 max-w-[280px] min-w-[200px] flex flex-col gap-2 animate-in fade-in zoom-in-95 duration-100",style:{transform:`translateX(calc(-50% + ${te.shiftX}px))`},children:[e.jsxs("div",{className:"flex items-center justify-between border-b border-oai-gray-100 dark:border-oai-gray-800/80 pb-1.5",children:[e.jsx("span",{className:"text-[11px] font-semibold text-oai-gray-500 dark:text-oai-gray-400",children:N.day}),(()=>{const a=N.level===0?E?"#9ca3af":"#6b7280":L[N.level];return e.jsxs("span",{className:"text-[10px] px-2 py-0.5 rounded-full font-medium",style:{backgroundColor:a+"22",color:a,border:`1px solid ${a}44`},children:["Level ",N.level]})})()]}),e.jsxs("div",{className:"flex flex-col gap-2",children:[e.jsxs("div",{className:"flex items-baseline gap-1",children:[e.jsx("span",{className:"text-lg font-bold text-oai-gray-900 dark:text-white leading-none",children:Number(N.total_tokens??N.value).toLocaleString()}),e.jsx("span",{className:"text-[10px] text-oai-gray-400 uppercase tracking-wider font-semibold",children:"Tokens"})]}),N.models&&Object.keys(N.models).length>0?e.jsxs("div",{className:"mt-1.5 border-t border-oai-gray-100 dark:border-oai-gray-800/60 pt-2 flex flex-col gap-1.5",children:[e.jsx("div",{className:"text-[10px] font-semibold text-oai-gray-400 dark:text-oai-gray-500 uppercase tracking-wider",children:"Model Breakdown"}),e.jsx("div",{className:"flex flex-col gap-2 max-h-[150px] overflow-y-auto pr-1.5 oai-scrollbar",children:Object.entries(N.models).map(([a,u])=>({name:a,val:Number(u)})).sort((a,u)=>u.val-a.val).map(({name:a,val:u})=>{const T=Number(N.total_tokens??N.value)||1,v=Math.round(u/T*100);return e.jsxs("div",{className:"flex flex-col gap-1",children:[e.jsxs("div",{className:"flex items-center justify-between text-[11px] gap-3",children:[e.jsx("span",{className:"font-medium text-oai-gray-750 dark:text-oai-gray-200 truncate max-w-[120px]",title:a,children:a}),e.jsxs("div",{className:"flex items-center gap-1.5 shrink-0",children:[e.jsx("span",{className:"font-mono text-oai-gray-900 dark:text-oai-gray-100 font-semibold",children:u.toLocaleString()}),e.jsxs("span",{className:"text-[9px] text-oai-gray-450 dark:text-oai-gray-500 min-w-[28px] text-right font-medium",children:[v,"%"]})]})]}),e.jsx("div",{className:"w-full h-1 bg-oai-gray-100 dark:bg-oai-gray-800/85 rounded-full overflow-hidden",children:e.jsx("div",{className:"h-full rounded-full transition-all duration-300",style:{width:`${v}%`,backgroundColor:L[4],boxShadow:`0 0 4px ${L[4]}55`}})})]},a)})})]}):e.jsx("p",{className:"text-[11px] text-oai-gray-600 dark:text-oai-gray-300 leading-relaxed font-normal mt-1 border-t border-dashed border-oai-gray-100 dark:border-oai-gray-800/60 pt-1.5",children:Se(N.level,N.total_tokens??N.value)})]})]}),e.jsx("div",{className:"absolute bottom-[6px] left-0 -translate-x-1/2 w-2.5 h-2.5 rotate-45 bg-white dark:bg-oai-gray-900 border-r border-b border-oai-gray-200/50 dark:border-oai-gray-800/50 shadow-sm",style:{marginBottom:"1px"}})]}),document.body),e.jsx("style",{children:`
2
- @keyframes tt-fade-in {
3
- from { opacity: 0; }
4
- to { opacity: 1; }
5
- }
6
- @keyframes tt-fade-out {
7
- from { opacity: 1; }
8
- to { opacity: 0; }
9
- }
10
- @keyframes tt-modal-entrance {
11
- from {
12
- opacity: 0;
13
- transform: scale(0.96) translateY(10px);
14
- }
15
- to {
16
- opacity: 1;
17
- transform: scale(1) translateY(0);
18
- }
19
- }
20
- @keyframes tt-modal-exit {
21
- from {
22
- opacity: 1;
23
- transform: scale(1) translateY(0);
24
- }
25
- to {
26
- opacity: 0;
27
- transform: scale(0.96) translateY(10px);
28
- }
29
- }
30
- .animate-tt-fade-in {
31
- animation: tt-fade-in 0.2s cubic-bezier(0.16, 1, 0.3, 1) forwards;
32
- }
33
- .animate-tt-fade-out {
34
- animation: tt-fade-out 0.2s cubic-bezier(0.16, 1, 0.3, 1) forwards;
35
- }
36
- .animate-tt-modal {
37
- animation: tt-modal-entrance 0.3s cubic-bezier(0.34, 1.3, 0.64, 1) forwards;
38
- }
39
- .animate-tt-modal-exit {
40
- animation: tt-modal-exit 0.2s cubic-bezier(0.16, 1, 0.3, 1) forwards;
41
- }
42
- `})]})}export{vt as A,Ve as M,nt as T};