tokentracker-cli 0.83.10 → 0.84.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. package/dashboard/dist/assets/{AchievementBadge-XEgOYdAL.js → AchievementBadge-3L7czzR1.js} +1 -1
  2. package/dashboard/dist/assets/AchievementsPage-BFfuj5At.js +2 -0
  3. package/dashboard/dist/assets/{AchievementsSection-DVba9yNb.js → AchievementsSection-HYShnmyO.js} +1 -1
  4. package/dashboard/dist/assets/BadgeDetailModal-CCSn5R-f.js +1 -0
  5. package/dashboard/dist/assets/{Card-Su_MA3H3.js → Card-DXegrNGi.js} +1 -1
  6. package/dashboard/dist/assets/{ClawdAnimated-ISeGRtL0.js → ClawdAnimated-D72waMCF.js} +1 -1
  7. package/dashboard/dist/assets/CommandPalette-BMng2nFX.js +1 -0
  8. package/dashboard/dist/assets/CommunityStatsModal-BQmxfHSr.js +1 -0
  9. package/dashboard/dist/assets/{ConfirmModal-DcumD9hD.js → ConfirmModal-CpyPGo-h.js} +1 -1
  10. package/dashboard/dist/assets/DashboardPage-CQ2A9jo4.js +1 -0
  11. package/dashboard/dist/assets/DevicePage-DG0Vxsww.js +1 -0
  12. package/dashboard/dist/assets/DialogClose-DJ8mCX28.js +1 -0
  13. package/dashboard/dist/assets/DialogDescription-CJDYVJ8S.js +1 -0
  14. package/dashboard/dist/assets/DialogTitle-1vR-kqKB.js +1 -0
  15. package/dashboard/dist/assets/{FadeIn-BDbcrkQ9.js → FadeIn-DHkAS7mo.js} +1 -1
  16. package/dashboard/dist/assets/{HeaderGithubStar-BzMdQsiQ.js → HeaderGithubStar-DR0AXuPQ.js} +1 -1
  17. package/dashboard/dist/assets/HoverTooltip-CcGvGB5a.js +1 -0
  18. package/dashboard/dist/assets/{IpCheckPage-CjAfUsej.js → IpCheckPage-DycvTdue.js} +1 -1
  19. package/dashboard/dist/assets/LandingPage-CHs8wugk.js +2 -0
  20. package/dashboard/dist/assets/{LeaderboardAvatar-Bi3swTRk.js → LeaderboardAvatar-CN9dR1b7.js} +1 -1
  21. package/dashboard/dist/assets/LeaderboardPage-BLXve5xc.js +2 -0
  22. package/dashboard/dist/assets/{LeaderboardProfileModal-DsSL1GnS.js → LeaderboardProfileModal-CarZtrz9.js} +4 -4
  23. package/dashboard/dist/assets/{LeaderboardProfilePage-Bj9boJ81.js → LeaderboardProfilePage-DAoBzt2h.js} +3 -3
  24. package/dashboard/dist/assets/LimitsPage-DFn8Z9MC.js +4 -0
  25. package/dashboard/dist/assets/{LocalOnlyNotice-Bx7JAQlv.js → LocalOnlyNotice-H0JRSn1e.js} +1 -1
  26. package/dashboard/dist/assets/LoginCard-wd3R-8P7.js +1 -0
  27. package/dashboard/dist/assets/{LoginModal-ZkdEDB_u.js → LoginModal-DYXk0bmO.js} +1 -1
  28. package/dashboard/dist/assets/{LoginPage-cZjVN1Ey.js → LoginPage-DxN9qFIi.js} +1 -1
  29. package/dashboard/dist/assets/PetPage-BjZIfiI3.js +1 -0
  30. package/dashboard/dist/assets/PopoverPopup-CQIKHlVw.js +1 -0
  31. package/dashboard/dist/assets/{ProviderIcon-Dd7G999Z.js → ProviderIcon-BrrTOC6_.js} +1 -1
  32. package/dashboard/dist/assets/{ResetPasswordPage-BQnvwZHh.js → ResetPasswordPage-BNwsqyqJ.js} +1 -1
  33. package/dashboard/dist/assets/SegmentedControl-cSXGJx_j.js +1 -0
  34. package/dashboard/dist/assets/Select-D5H9WvyT.js +1 -0
  35. package/dashboard/dist/assets/SelectItemText-fSnMRQbs.js +1 -0
  36. package/dashboard/dist/assets/SessionsPage-CXF87EFA.js +2 -0
  37. package/dashboard/dist/assets/SettingsPage-Hmqe_c9L.js +1 -0
  38. package/dashboard/dist/assets/SkillsPage-CpL3mDUz.js +1 -0
  39. package/dashboard/dist/assets/{TokenGalaxy-DLJDFTqi.js → TokenGalaxy-CnutAaeA.js} +2 -2
  40. package/dashboard/dist/assets/ToolbarRootContext-CbdtTEGs.js +1 -0
  41. package/dashboard/dist/assets/TrendMonitor-CfbNLJ9Z.js +19 -0
  42. package/dashboard/dist/assets/WidgetsPage-DOCARdp-.js +1 -0
  43. package/dashboard/dist/assets/{WrappedPage-DfGBWz1l.js → WrappedPage-BKNZTfkp.js} +1 -1
  44. package/dashboard/dist/assets/__vite-browser-external-CvrVYYUZ.js +1465 -0
  45. package/dashboard/dist/assets/__vite-browser-external-WKilYZnq.css +1 -0
  46. package/dashboard/dist/assets/{agent-logos-GKeC1zIo.js → agent-logos-CvFfhBDq.js} +1 -1
  47. package/dashboard/dist/assets/{arrow-up-right-CtbeXi5j.js → arrow-up-right-BtBLF2n1.js} +1 -1
  48. package/dashboard/dist/assets/{check-CNrLvSi6.js → check-DfPQe0v9.js} +1 -1
  49. package/dashboard/dist/assets/chevron-right-isIHsrvd.js +1 -0
  50. package/dashboard/dist/assets/{copy-BKtI8qFQ.js → copy-CJrwu3Xh.js} +1 -1
  51. package/dashboard/dist/assets/{download-CYBO92ti.js → download-D-TaSqSP.js} +1 -1
  52. package/dashboard/dist/assets/{flame-DhLu4C_0.js → flame-95I2KBCp.js} +1 -1
  53. package/dashboard/dist/assets/{format-tokens-Chz9fmBF.js → format-tokens-D0_AljQl.js} +1 -1
  54. package/dashboard/dist/assets/host-mode-Bc_iTzHk.js +1 -0
  55. package/dashboard/dist/assets/{icons-BSHx86zo.js → icons-B0jhI6as.js} +1 -1
  56. package/dashboard/dist/assets/{index-B6zXwIZo.js → index-DNueKe7x.js} +1 -1
  57. package/dashboard/dist/assets/{index-CT2TNtwr.js → index-Do9pBHr0.js} +1 -1
  58. package/dashboard/dist/assets/{index-Dh6afgQA.js → index-zL31qR6U.js} +1 -1
  59. package/dashboard/dist/assets/{info-hP16Kjuo.js → info-DXinzd-a.js} +1 -1
  60. package/dashboard/dist/assets/{limits-providers-DW6OXuoF.js → limits-providers-DEb3zmKt.js} +1 -1
  61. package/dashboard/dist/assets/{link-2-BPvDNkc7.js → link-2-BVAdYWaq.js} +1 -1
  62. package/dashboard/dist/assets/{loader-circle-ZoO_8a9T.js → loader-circle-nu9zNFC8.js} +1 -1
  63. package/dashboard/dist/assets/maximize-2-D3RclWuw.js +1 -0
  64. package/dashboard/dist/assets/provider-display-ChB_33mn.js +42 -0
  65. package/dashboard/dist/assets/{react-hAUBl17Q.js → react-Djyp9kg9.js} +1 -1
  66. package/dashboard/dist/assets/search-CRzn5fb2.js +1 -0
  67. package/dashboard/dist/assets/skills-api-D4df2IJr.js +1 -0
  68. package/dashboard/dist/assets/store-BE4jimvO.js +1 -0
  69. package/dashboard/dist/assets/terminal-C8bbfANZ.js +1 -0
  70. package/dashboard/dist/assets/{trash-2-DepTfc8X.js → trash-2-B1pgRqNS.js} +1 -1
  71. package/dashboard/dist/assets/{use-community-stats-DTnZGJZ0.js → use-community-stats-Co5GxKNb.js} +1 -1
  72. package/dashboard/dist/assets/{use-limits-display-prefs-Dg8vOFKh.js → use-limits-display-prefs-CVxmTm05.js} +1 -1
  73. package/dashboard/dist/assets/{use-native-settings-BesbYZlC.js → use-native-settings-C3cNbS0b.js} +1 -1
  74. package/dashboard/dist/assets/{use-ordered-list-Bsr3rffj.js → use-ordered-list-DqGSSTpQ.js} +1 -1
  75. package/dashboard/dist/assets/use-reduced-motion-D7Wk0w1o.js +1 -0
  76. package/dashboard/dist/assets/{use-session-efficiency-pref-DGzTkmva.js → use-session-efficiency-pref-w80Pa5Dd.js} +1 -1
  77. package/dashboard/dist/assets/{use-transform-IS87eWhx.js → use-transform-DxH5_rBe.js} +1 -1
  78. package/dashboard/dist/assets/{use-usage-limits-56vprzF2.js → use-usage-limits-BY0LkALC.js} +1 -1
  79. package/dashboard/dist/assets/{useCurrency-CzLaB8v2.js → useCurrency-CG55rarM.js} +1 -1
  80. package/dashboard/dist/assets/useScrollLock-BjjtVeB5.js +1 -0
  81. package/dashboard/dist/assets/{useTokenFormat-BJw_Glux.js → useTokenFormat-ChaFYAh3.js} +1 -1
  82. package/dashboard/dist/assets/{zap-D2t6DGdL.js → zap-Doeerimb.js} +1 -1
  83. package/dashboard/dist/index.html +2 -2
  84. package/dashboard/dist/ip-check.html +2 -2
  85. package/dashboard/dist/leaderboard.html +2 -2
  86. package/dashboard/dist/share.html +2 -2
  87. package/package.json +1 -1
  88. package/src/commands/init.js +5 -6
  89. package/src/commands/status.js +10 -9
  90. package/src/lib/codex-config.js +45 -4
  91. package/src/lib/local-api.js +30 -0
  92. package/src/lib/pricing/curated-overrides.json +4 -0
  93. package/src/lib/pricing/seed-snapshot.json +1 -1
  94. package/src/lib/session-analytics.js +336 -25
  95. package/dashboard/dist/assets/AchievementsPage-BOMLm09Y.js +0 -2
  96. package/dashboard/dist/assets/BadgeDetailModal-Bg-AL5Y_.js +0 -1
  97. package/dashboard/dist/assets/CommandPalette-C1x51Cx1.js +0 -1
  98. package/dashboard/dist/assets/CommunityStatsModal-wgD16fT2.js +0 -1
  99. package/dashboard/dist/assets/DashboardPage-CSNTFnUG.js +0 -1
  100. package/dashboard/dist/assets/DevicePage-Djo2Bqsg.js +0 -1
  101. package/dashboard/dist/assets/DialogClose-Cufbk4wh.js +0 -1
  102. package/dashboard/dist/assets/DialogDescription-B6gmUT6p.js +0 -1
  103. package/dashboard/dist/assets/DialogTitle-BYRgo5mK.js +0 -1
  104. package/dashboard/dist/assets/LandingPage-Bv7_h1mo.js +0 -2
  105. package/dashboard/dist/assets/LeaderboardPage-CTbzru_a.js +0 -2
  106. package/dashboard/dist/assets/LimitsPage-DvqHF8NR.js +0 -4
  107. package/dashboard/dist/assets/LoginCard-CiLaG4Dr.js +0 -1
  108. package/dashboard/dist/assets/PetPage-CK9dvzKU.js +0 -1
  109. package/dashboard/dist/assets/PopoverPopup-Dq4uabm_.js +0 -1
  110. package/dashboard/dist/assets/Select-DAXwXsS4.js +0 -1
  111. package/dashboard/dist/assets/SelectItemText-DBPZbDEO.js +0 -1
  112. package/dashboard/dist/assets/SettingsPage-BDk6B2wL.js +0 -1
  113. package/dashboard/dist/assets/SkillsPage-Bk-WRE5b.js +0 -1
  114. package/dashboard/dist/assets/TrendMonitor-CRxumE5w.js +0 -19
  115. package/dashboard/dist/assets/WidgetsPage-HZq9kUv1.js +0 -1
  116. package/dashboard/dist/assets/__vite-browser-external-BEMfOISl.css +0 -1
  117. package/dashboard/dist/assets/__vite-browser-external-CDtmmXRU.js +0 -1418
  118. package/dashboard/dist/assets/provider-display-CvC5QtLF.js +0 -42
  119. package/dashboard/dist/assets/skills-api-DSMG8EyE.js +0 -1
  120. package/dashboard/dist/assets/terminal-BP0gsSiR.js +0 -1
  121. package/dashboard/dist/assets/use-reduced-motion-_YIc4wth.js +0 -1
  122. package/dashboard/dist/assets/useScrollLock-DU8uPOsl.js +0 -1
@@ -15,7 +15,21 @@ const { computeRowCost } = require("./pricing");
15
15
 
16
16
  // Bump the sidecar when derived metrics change so cached rows are rebuilt
17
17
  // instead of leaving the dashboard on the previous (over-counted) heuristic.
18
- const SIDECAR_VERSION = 6;
18
+ // v7 adds the raw session_id (local-only, needed to build resume commands for
19
+ // the session browser); it never leaves the Node process through the cloud.
20
+ // v8 adds an agent-authored one-line title: Claude's "ai-title" session-log
21
+ // record and Codex's thread_name from session_index.jsonl. Both are metadata
22
+ // the agent wrote itself, never the raw prompt body, and stay local-only
23
+ // alongside session_id (no self-parsed prompt fallback, so the strict
24
+ // metadata-only guarantee holds).
25
+ // v9 changes derived metrics, so every cached row must be rebuilt:
26
+ // - duration_ms is now *active* time (see IDLE_GAP_MS), not the wall-clock
27
+ // span between the first and last log line, which read as 89 days on
28
+ // resumed/forked sessions.
29
+ // - session_id is only set when the log actually contained a sessionId
30
+ // record. It used to fall back to the file's basename, which produced
31
+ // resume commands for non-session files (`claude --resume journal`).
32
+ const SIDECAR_VERSION = 9;
19
33
  const EDIT_TOOLS = new Set(["apply_patch", "edit", "write", "multiedit", "notebookedit"]);
20
34
  const PLACEHOLDER_MODELS = new Set(["<synthetic>", "synthetic", "<unknown>", "unknown"]);
21
35
  const CLAUDE_MEM_OBSERVER_PROJECT_SUFFIX = "--claude-mem-observer-sessions";
@@ -67,11 +81,39 @@ function safeTimestamp(value) {
67
81
  return new Date(value).toISOString();
68
82
  }
69
83
 
84
+ // A session that was resumed days later, or whose file replays inherited
85
+ // history from a fork, spans a wall-clock range that says nothing about how
86
+ // long the user actually worked (observed: 2142h / 89 days on a session with
87
+ // zero turns). Accumulate *active* time instead: the sum of gaps between
88
+ // consecutive log timestamps, excluding any gap longer than IDLE_GAP_MS. The
89
+ // long jump from replayed history to real work is one such gap, so it drops
90
+ // out on its own. started_at / ended_at still carry the true span.
91
+ const IDLE_GAP_MS = 30 * 60 * 1000;
92
+
93
+ function emptyBounds() {
94
+ return { started_at: null, ended_at: null, active_ms: 0, _last_ts_ms: null };
95
+ }
96
+
70
97
  function updateBounds(bounds, value) {
71
98
  const timestamp = safeTimestamp(value);
72
99
  if (!timestamp) return;
73
100
  if (!bounds.started_at || timestamp < bounds.started_at) bounds.started_at = timestamp;
74
101
  if (!bounds.ended_at || timestamp > bounds.ended_at) bounds.ended_at = timestamp;
102
+ const ms = Date.parse(timestamp);
103
+ if (!Number.isFinite(ms)) return;
104
+ if (Number.isFinite(bounds._last_ts_ms)) {
105
+ const delta = ms - bounds._last_ts_ms;
106
+ // Ignore out-of-order lines and idle gaps; only real working time counts.
107
+ if (delta > 0 && delta <= IDLE_GAP_MS) bounds.active_ms += delta;
108
+ }
109
+ bounds._last_ts_ms = ms;
110
+ }
111
+
112
+ // Throwaway per-agent checkouts (`.claude/worktrees/<id>`, `.codex/worktrees/…`)
113
+ // are not the project the user is working on, and the directory is usually gone
114
+ // by the time the session is browsed.
115
+ function isWorktreeRef(ref) {
116
+ return /[\\/](worktrees|subagents)[\\/]/.test(String(ref || ""));
75
117
  }
76
118
 
77
119
  function projectKey(cwd, filePath) {
@@ -103,6 +145,17 @@ function promptFingerprint(prompt) {
103
145
  return crypto.createHash("sha256").update(prompt.replace(/\s+/g, " ")).digest("hex");
104
146
  }
105
147
 
148
+ // Normalize a session title into a single short line. Titles come ONLY from
149
+ // the agent's own metadata (Claude's ai-title, Codex's thread_name) — never
150
+ // the raw prompt body — so the metadata-only privacy guarantee holds. They
151
+ // stay local-only alongside session_id.
152
+ function cleanSessionTitle(value) {
153
+ if (typeof value !== "string") return null;
154
+ const text = value.replace(/\s+/g, " ").trim();
155
+ if (!text) return null;
156
+ return text.length > 120 ? `${text.slice(0, 117).trimEnd()}…` : text;
157
+ }
158
+
106
159
  function extractCodexPrompt(obj) {
107
160
  if (obj?.type !== "event_msg" || obj.payload?.type !== "user_message") return null;
108
161
  if (typeof obj.payload.message === "string") {
@@ -144,11 +197,9 @@ function extractCodexSignalTools(payload) {
144
197
  }
145
198
 
146
199
  function finalizeRecord(record) {
147
- const startedMs = Date.parse(record.started_at || "");
148
- const endedMs = Date.parse(record.ended_at || "");
149
- record.duration_ms = Number.isFinite(startedMs) && Number.isFinite(endedMs)
150
- ? Math.max(0, endedMs - startedMs)
151
- : 0;
200
+ delete record._last_ts_ms;
201
+ record.active_ms = Math.max(0, finite(record.active_ms));
202
+ record.duration_ms = record.active_ms;
152
203
  record.total_tokens = finite(record.total_tokens || record.tokens?.total_tokens);
153
204
  record.cost_usd = computeRowCost({ source: record.source, model: record.model, ...record.tokens });
154
205
  record.productive = record.edit_turns > 0;
@@ -166,11 +217,29 @@ async function scanClaudeSession(filePath) {
166
217
  const input = fs.createReadStream(filePath, { encoding: "utf8" });
167
218
  const lines = readline.createInterface({ input, crlfDelay: Infinity });
168
219
  const tokens = emptyTotals();
220
+ // Per-file, deliberately. rollout.js persists its dedup set across files to
221
+ // catch cross-file duplicates, but that is only sound there because it owns a
222
+ // single global total. Here each file becomes an independently cached row, so
223
+ // a cross-file set would make a row's tokens depend on which other files were
224
+ // scanned first — and the incremental cache reuses rows without replaying
225
+ // that state. Measured cost of the per-file scope on real data: 808 assistant
226
+ // messages appear in more than one file (fork/resume replays history into a
227
+ // new session file), inflating per-session tokens by ~1.5% in aggregate.
228
+ // Fixing it needs a deterministic owner (e.g. earliest started_at) plus
229
+ // message keys persisted per row — a sidecar redesign, not a one-liner. Do
230
+ // NOT "fix" this by widening the set without solving the cache interaction.
169
231
  const seenMessages = new Set();
170
- const bounds = { started_at: null, ended_at: null };
232
+ const bounds = emptyBounds();
233
+ // The basename keeps grouping stable for files that never write a sessionId
234
+ // record, but only an *observed* sessionId may become a resumable
235
+ // session_id — otherwise non-session logs under ~/.claude/projects (e.g.
236
+ // skill-injections.jsonl, journal.jsonl) yield `claude --resume journal`,
237
+ // a command that always fails.
171
238
  let rawSessionId = path.basename(filePath, ".jsonl");
239
+ let observedSessionId = null;
172
240
  let cwd = null;
173
241
  let model = "unknown";
242
+ let aiTitle = null;
174
243
  let turns = 0;
175
244
  let editTurns = 0;
176
245
  let retryTurns = 0;
@@ -190,8 +259,18 @@ async function scanClaudeSession(filePath) {
190
259
  let obj;
191
260
  try { obj = JSON.parse(line); } catch { continue; }
192
261
  updateBounds(bounds, obj.timestamp || obj.message?.timestamp);
193
- if (typeof obj.sessionId === "string" && obj.sessionId) rawSessionId = obj.sessionId;
262
+ if (typeof obj.sessionId === "string" && obj.sessionId) {
263
+ rawSessionId = obj.sessionId;
264
+ observedSessionId = obj.sessionId;
265
+ }
194
266
  if (typeof obj.cwd === "string" && obj.cwd) cwd = obj.cwd;
267
+ // Claude writes its own generated one-line summary as an "ai-title" record.
268
+ // It is agent-authored metadata (not the raw prompt body); keep the latest.
269
+ if (obj.type === "ai-title" && typeof obj.aiTitle === "string") {
270
+ const cleaned = cleanSessionTitle(obj.aiTitle);
271
+ if (cleaned) aiTitle = cleaned;
272
+ continue;
273
+ }
195
274
  if (obj.type === "user") {
196
275
  const prompt = extractClaudePrompt(obj);
197
276
  if (!prompt) continue;
@@ -231,6 +310,12 @@ async function scanClaudeSession(filePath) {
231
310
  return finalizeRecord({
232
311
  version: SIDECAR_VERSION,
233
312
  session_hash: sessionHash("claude", rawSessionId),
313
+ session_id: observedSessionId || null,
314
+ // Claude's own generated one-line title (the "ai-title" record). Agent-
315
+ // authored metadata, never the raw prompt body. Local-only: stripped in
316
+ // summarizeSessions before any cloud/CSV export. Null when Claude never
317
+ // wrote one — the UI then falls back to the project name.
318
+ title: aiTitle,
234
319
  source: "claude",
235
320
  project_key: projectKey(cwd, filePath),
236
321
  project_ref: cwd || null,
@@ -247,7 +332,7 @@ async function scanClaudeSession(filePath) {
247
332
  }
248
333
 
249
334
  async function scanCodexDeliverySignals(filePath) {
250
- const bounds = { started_at: null, ended_at: null };
335
+ const bounds = emptyBounds();
251
336
  const input = fs.createReadStream(filePath, { encoding: "utf8" });
252
337
  const lines = readline.createInterface({ input, crlfDelay: Infinity });
253
338
  let turns = 0;
@@ -327,9 +412,17 @@ async function scanCodexSession(filePath) {
327
412
  const model = parsedModel && parsedModel.toLowerCase() !== provider?.toLowerCase()
328
413
  ? parsedModel
329
414
  : "unknown";
415
+ // Codex's own thread title from session_index.jsonl (Codex-authored
416
+ // metadata, keyed by session id). Null when Codex never named the thread —
417
+ // the UI then falls back to the project name.
418
+ const titleIndex = loadCodexTitleIndex(filePath);
419
+ const title = (parsed.sessionId && titleIndex.get(parsed.sessionId)) || null;
330
420
  return finalizeRecord({
331
421
  version: SIDECAR_VERSION,
332
422
  session_hash: sessionHash("codex", parsed.sessionId || filePath),
423
+ session_id: parsed.sessionId || null,
424
+ // Local-only: stripped in summarizeSessions before any cloud/CSV export.
425
+ title,
333
426
  source: "codex",
334
427
  project_key: projectKey(parsed.cwd, filePath),
335
428
  project_ref: parsed.cwd || null,
@@ -398,6 +491,50 @@ function sessionFileStatKey(filePath) {
398
491
  }
399
492
  }
400
493
 
494
+ // Codex records its own per-thread title (thread_name) in
495
+ // ~/.codex/session_index.jsonl (`{ id, thread_name, updated_at }`). We read it
496
+ // once per build and memoize by the index's stat so repeated scans are cheap.
497
+ const codexTitleIndexCache = new Map();
498
+
499
+ function codexTitleIndexPathFor(filePath) {
500
+ const parts = path.resolve(filePath).split(path.sep);
501
+ const idx = parts.lastIndexOf(".codex");
502
+ if (idx === -1) return null;
503
+ return [...parts.slice(0, idx + 1), "session_index.jsonl"].join(path.sep);
504
+ }
505
+
506
+ function loadCodexTitleIndex(filePath) {
507
+ const indexPath = codexTitleIndexPathFor(filePath);
508
+ if (!indexPath) return new Map();
509
+ const statKey = sessionFileStatKey(indexPath);
510
+ const cached = codexTitleIndexCache.get(indexPath);
511
+ if (cached && cached.statKey === statKey) return cached.titles;
512
+ const titles = new Map();
513
+ if (statKey) {
514
+ try {
515
+ for (const line of fs.readFileSync(indexPath, "utf8").split("\n")) {
516
+ if (!line.trim()) continue;
517
+ let obj;
518
+ try { obj = JSON.parse(line); } catch { continue; }
519
+ const id = typeof obj?.id === "string" ? obj.id : null;
520
+ const name = cleanSessionTitle(obj?.thread_name);
521
+ if (id && name) titles.set(id, name);
522
+ }
523
+ } catch { /* no index yet */ }
524
+ }
525
+ codexTitleIndexCache.set(indexPath, { statKey, titles });
526
+ return titles;
527
+ }
528
+
529
+ // A Codex row also depends on session_index.jsonl, not just its rollout file.
530
+ // Include that dependency in the incremental cache key so renaming a thread is
531
+ // visible after the next refresh instead of leaving a stale title indefinitely.
532
+ function analyticsEntryStatKey(source, filePath) {
533
+ const sessionStat = sessionFileStatKey(filePath);
534
+ if (!sessionStat || source !== "codex") return sessionStat;
535
+ return `${sessionStat}|title-index:${sessionFileStatKey(codexTitleIndexPathFor(filePath)) || "missing"}`;
536
+ }
537
+
401
538
  function readSidecar(sidecarPath) {
402
539
  try {
403
540
  return fs.readFileSync(sidecarPath, "utf8").split("\n").filter(Boolean).map((line) => JSON.parse(line));
@@ -429,7 +566,14 @@ async function buildSessionAnalyticsInternal({ home = os.homedir(), force = fals
429
566
  } catch { /* first run */ }
430
567
  }
431
568
  const discovered = await discoverSessionFiles(home);
432
- const signature = filesSignature([...discovered.claude, ...discovered.codex]);
569
+ // Codex thread titles are stored separately from rollout files. Include the
570
+ // index in the overall signature so an index-only rename reaches the
571
+ // per-file dependency check below on the next refresh.
572
+ const signature = filesSignature([
573
+ ...discovered.claude,
574
+ ...discovered.codex,
575
+ ...discovered.codex.map(codexTitleIndexPathFor).filter(Boolean),
576
+ ]);
433
577
  if (!force && previousMeta?.version === SIDECAR_VERSION && previousMeta.signature === signature) {
434
578
  await writeAtomic(metaPath, `${JSON.stringify({ ...previousMeta, checked_at: new Date().toISOString() })}\n`);
435
579
  return readSidecar(sidecarPath);
@@ -450,16 +594,32 @@ async function buildSessionAnalyticsInternal({ home = os.homedir(), force = fals
450
594
  ...discovered.claude.map((filePath) => ({ source: "claude", filePath, scan: scanClaudeSession })),
451
595
  ...discovered.codex.map((filePath) => ({ source: "codex", filePath, scan: scanCodexSession })),
452
596
  ];
597
+ // Files we could not turn into a row (permission denied, half-written line,
598
+ // vanished mid-scan). Swallowing these silently made sessions disappear with
599
+ // no signal at all; count them so the API can say so.
600
+ let skippedFiles = 0;
453
601
  for (const entry of entries) {
454
602
  const cacheKey = sessionFileCacheKey(entry.source, entry.filePath);
455
- const statKey = sessionFileStatKey(entry.filePath);
456
- if (!statKey) continue;
603
+ const statKey = analyticsEntryStatKey(entry.source, entry.filePath);
604
+ if (!statKey) {
605
+ skippedFiles += 1;
606
+ continue;
607
+ }
457
608
  let row = null;
458
609
  if (!force && previousFiles[cacheKey]?.stat_key === statKey) {
459
610
  row = previousRowsByFile.get(cacheKey) || null;
460
611
  }
461
612
  if (!row) {
462
- try { row = await entry.scan(entry.filePath); } catch { /* one active/partial session must not poison the sidecar */ }
613
+ try {
614
+ row = await entry.scan(entry.filePath);
615
+ } catch (error) {
616
+ // One active/partial session must not poison the sidecar, but do not
617
+ // pretend it never existed either.
618
+ skippedFiles += 1;
619
+ if (!process.env.NODE_TEST_CONTEXT) {
620
+ console.warn(`[session-analytics] skipped ${entry.source} session file: ${error?.message || error}`);
621
+ }
622
+ }
463
623
  }
464
624
  if (!row) continue;
465
625
  // A one-way file hash enables incremental reuse without persisting or
@@ -479,6 +639,9 @@ async function buildSessionAnalyticsInternal({ home = os.homedir(), force = fals
479
639
  checked_at: generatedAt,
480
640
  files: nextFiles,
481
641
  })}\n`);
642
+ // Non-enumerable so the array still behaves exactly like a plain row list
643
+ // for every existing caller (map/filter/JSON of the rows is unaffected).
644
+ Object.defineProperty(sessions, "skippedFiles", { value: skippedFiles, enumerable: false });
482
645
  return sessions;
483
646
  }
484
647
 
@@ -491,23 +654,41 @@ const sessionAnalyticsBuilds = new Map();
491
654
  function buildSessionAnalytics(options = {}) {
492
655
  const normalizedOptions = options && typeof options === "object" ? options : {};
493
656
  const home = path.resolve(String(normalizedOptions.home || os.homedir()));
657
+ const force = Boolean(normalizedOptions.force);
494
658
  const existing = sessionAnalyticsBuilds.get(home);
495
- if (existing) return existing;
496
-
497
- const promise = buildSessionAnalyticsInternal({ ...normalizedOptions, home });
498
- sessionAnalyticsBuilds.set(home, promise);
659
+ // Joining an in-flight build is only correct when that build is at least as
660
+ // thorough as what this caller asked for. A refresh that landed while a plain
661
+ // build was running used to be handed the un-refreshed result: the spinner
662
+ // stopped and nothing had actually been re-scanned. Chain instead of joining
663
+ // — never run two scans at once, they race the atomic sidecar write.
664
+ if (existing && (!force || existing.force)) return existing.promise;
665
+
666
+ const run = () => buildSessionAnalyticsInternal({ ...normalizedOptions, home, force });
667
+ const promise = existing ? existing.promise.then(run, run) : run();
668
+ const entry = { promise, force: force || Boolean(existing?.force) };
669
+ sessionAnalyticsBuilds.set(home, entry);
499
670
  const clear = () => {
500
- if (sessionAnalyticsBuilds.get(home) === promise) sessionAnalyticsBuilds.delete(home);
671
+ if (sessionAnalyticsBuilds.get(home) === entry) sessionAnalyticsBuilds.delete(home);
501
672
  };
502
673
  promise.then(clear, clear);
503
674
  return promise;
504
675
  }
505
676
 
677
+ // Does a session overlap the [from, to] day window? Testing started_at alone
678
+ // dropped every session that began before the window and ended inside it —
679
+ // exactly the long/resumed ones, which also sort to the top because the sort
680
+ // key is ended_at. On real data a 7-day window lost 6 sessions, the largest
681
+ // 224M tokens. Treat the window as an interval intersection instead.
682
+ function withinDayRange(row, from, to) {
683
+ const startDay = String(row?.started_at || row?.ended_at || "").slice(0, 10);
684
+ const endDay = String(row?.ended_at || row?.started_at || "").slice(0, 10);
685
+ if (from && (!endDay || endDay < from)) return false;
686
+ if (to && (!startDay || startDay > to)) return false;
687
+ return true;
688
+ }
689
+
506
690
  function summarizeSessions(sessions, { from = "", to = "", includeSessions = true } = {}) {
507
- const filtered = (sessions || []).filter((row) => {
508
- const day = String(row.started_at || row.ended_at || "").slice(0, 10);
509
- return (!from || day >= from) && (!to || day <= to);
510
- });
691
+ const filtered = (sessions || []).filter((row) => withinDayRange(row, from, to));
511
692
  const byModel = new Map();
512
693
  const subagents = new Map();
513
694
  for (const row of filtered) {
@@ -565,10 +746,11 @@ function summarizeSessions(sessions, { from = "", to = "", includeSessions = tru
565
746
  }, { sessions: 0, productive_sessions: 0, one_shot_sessions: 0, edit_turns: 0, retries: 0, total_tokens: 0, cost_usd: 0, edit_tokens: 0, edit_cost_usd: 0 });
566
747
  return {
567
748
  available: filtered.length > 0,
568
- // Local filesystem paths are required internally for Git attribution but
569
- // never leave the Node process through API/CSV payloads.
749
+ // Local filesystem paths and raw session ids are required internally for
750
+ // Git attribution and the local-only session browser, but never leave the
751
+ // Node process through API/CSV payloads.
570
752
  sessions: includeSessions
571
- ? filtered.map(({ project_ref: _projectRef, _cache_key: _cacheKey, ...row }) => row)
753
+ ? filtered.map(({ project_ref: _projectRef, session_id: _sessionId, title: _title, _cache_key: _cacheKey, ...row }) => row)
572
754
  : [],
573
755
  session_count: filtered.length,
574
756
  summary: {
@@ -595,6 +777,133 @@ function summarizeSessions(sessions, { from = "", to = "", includeSessions = tru
595
777
  };
596
778
  }
597
779
 
780
+ // Build the resume command a user can run to continue a past session. The
781
+ // session id is a vendor-generated UUID and never contains user prose.
782
+ function resumeCommandFor(source, sessionId) {
783
+ const id = typeof sessionId === "string" ? sessionId.trim() : "";
784
+ // Session ids come from local log files, which can be edited by other local
785
+ // processes. Only emit an unquoted shell token so copying the suggested
786
+ // command can never smuggle whitespace, flags, or shell metacharacters into
787
+ // the user's terminal. Claude also has a small set of legacy non-UUID ids,
788
+ // so keep the accepted alphabet broader than UUID while still shell-safe.
789
+ if (!/^[A-Za-z0-9][A-Za-z0-9._:-]{0,199}$/.test(id)) return null;
790
+ if (source === "claude") return `claude --resume ${id}`;
791
+ if (source === "codex") return `codex resume ${id}`;
792
+ return null;
793
+ }
794
+
795
+ function toSessionBrowserRow(row) {
796
+ return {
797
+ session_hash: row.session_hash,
798
+ session_id: row.session_id || null,
799
+ title: row.title || null,
800
+ source: row.source,
801
+ project_key: row.project_key,
802
+ // project_ref (the local cwd) only ever travels over the local API so the
803
+ // browser can show where a session ran and compose a resume command.
804
+ project_ref: row.project_ref || null,
805
+ model: row.model,
806
+ started_at: row.started_at || null,
807
+ ended_at: row.ended_at || null,
808
+ duration_ms: finite(row.duration_ms),
809
+ turns: finite(row.turns),
810
+ edit_turns: finite(row.edit_turns),
811
+ retry_turns: finite(row.retry_turns),
812
+ subagent_calls: finite(row.subagent_calls),
813
+ total_tokens: finite(row.total_tokens),
814
+ cost_usd: finite(row.cost_usd),
815
+ productive: Boolean(row.productive),
816
+ first_pass: Boolean(row.first_pass ?? row.one_shot),
817
+ resume_command: resumeCommandFor(row.source, row.session_id),
818
+ };
819
+ }
820
+
821
+ // Claude writes several on-disk files for one logical session (resumes and
822
+ // sub-agent sidechains) that all carry the same session id, so scanning yields
823
+ // multiple rows sharing an identical session_hash. Merge those fragments into
824
+ // one resumable session for the browser: sum the (non-overlapping) per-file
825
+ // token/turn counts, span the timestamps, and keep the agent-authored title.
826
+ // This also gives the UI a stable, unique key per row.
827
+ function mergeSessionFragments(rows) {
828
+ const byHash = new Map();
829
+ for (const row of rows || []) {
830
+ const key = row.session_hash;
831
+ const cur = byHash.get(key);
832
+ if (!cur) {
833
+ byHash.set(key, { ...row, _repr_tokens: finite(row.total_tokens) });
834
+ continue;
835
+ }
836
+ cur.turns = finite(cur.turns) + finite(row.turns);
837
+ cur.edit_turns = finite(cur.edit_turns) + finite(row.edit_turns);
838
+ cur.retry_turns = finite(cur.retry_turns) + finite(row.retry_turns);
839
+ cur.subagent_calls = finite(cur.subagent_calls) + finite(row.subagent_calls);
840
+ cur.total_tokens = finite(cur.total_tokens) + finite(row.total_tokens);
841
+ cur.cost_usd = finite(cur.cost_usd) + finite(row.cost_usd);
842
+ cur.productive = Boolean(cur.productive) || Boolean(row.productive);
843
+ cur.active_ms = finite(cur.active_ms) + finite(row.active_ms);
844
+ if (!cur.title && row.title) cur.title = row.title;
845
+ // Representative model/project comes from the busiest fragment (most
846
+ // tokens) so a tiny sidechain cannot overwrite the real coding model.
847
+ if (finite(row.total_tokens) > finite(cur._repr_tokens)) {
848
+ cur._repr_tokens = finite(row.total_tokens);
849
+ if (row.model && row.model !== "unknown") cur.model = row.model;
850
+ // Project needs its own guard: a subagent running in a git worktree has
851
+ // that throwaway directory as its cwd, so the busiest fragment can label
852
+ // the whole session with a temp name like "agent-a3cb8089a11d3471a" and
853
+ // point project_ref at a directory that no longer exists. Only let a
854
+ // worktree fragment name the session when nothing better is available.
855
+ if (!isWorktreeRef(row.project_ref) || isWorktreeRef(cur.project_ref)) {
856
+ cur.project_key = row.project_key;
857
+ cur.project_ref = row.project_ref;
858
+ }
859
+ }
860
+ if (row.started_at && (!cur.started_at || row.started_at < cur.started_at)) cur.started_at = row.started_at;
861
+ if (row.ended_at && (!cur.ended_at || row.ended_at > cur.ended_at)) cur.ended_at = row.ended_at;
862
+ }
863
+ const merged = [];
864
+ for (const row of byHash.values()) {
865
+ delete row._repr_tokens;
866
+ // Active time is additive across fragments (see IDLE_GAP_MS); the
867
+ // wall-clock span between first and last line is not.
868
+ row.duration_ms = finite(row.active_ms);
869
+ row.first_pass = finite(row.edit_turns) === 1 && finite(row.retry_turns) === 0;
870
+ row.one_shot = row.first_pass;
871
+ merged.push(row);
872
+ }
873
+ return merged;
874
+ }
875
+
876
+ // Local-only session list for the dashboard session browser. Unlike
877
+ // summarizeSessions (cloud/CSV safe), this retains session_id + project_ref so
878
+ // the UI can offer one-click resume. Callers must only expose it over the
879
+ // local API.
880
+ function listSessionsForBrowser(sessions, { from = "", to = "", limit = 0 } = {}) {
881
+ const filtered = mergeSessionFragments(sessions)
882
+ // Only sessions that actually spent tokens are worth listing. This drops
883
+ // two kinds of noise: non-session logs under ~/.claude/projects
884
+ // (skill-injections.jsonl, journal.jsonl, …), and sessions abandoned before
885
+ // the model replied. Both render as "unknown · 0 tokens · $0.00" rows with
886
+ // no model, no cost and nothing to analyze.
887
+ .filter((row) => finite(row.total_tokens) > 0)
888
+ .filter((row) => withinDayRange(row, from, to));
889
+ filtered.sort((a, b) => String(b.ended_at || "").localeCompare(String(a.ended_at || "")));
890
+ const cap = Number(limit) > 0 ? Number(limit) : 0;
891
+ const limited = cap > 0 ? filtered.slice(0, cap) : filtered;
892
+ return {
893
+ available: filtered.length > 0,
894
+ session_count: filtered.length,
895
+ returned_count: limited.length,
896
+ skipped_files: finite(sessions?.skippedFiles),
897
+ sessions: limited.map(toSessionBrowserRow),
898
+ provenance: {
899
+ source: "local-session-log",
900
+ confidence: "observed",
901
+ privacy: "metadata-only",
902
+ scope: "local-only",
903
+ },
904
+ };
905
+ }
906
+
598
907
  function sessionsToCsv(rows) {
599
908
  const columns = ["session_hash", "source", "project_key", "model", "started_at", "ended_at", "duration_ms", "turns", "edit_turns", "retry_turns", "subagent_calls", "total_tokens", "cost_usd", "productive", "first_pass", "one_shot"];
600
909
  const escape = (value) => `"${String(value ?? "").replaceAll('"', '""')}"`;
@@ -608,5 +917,7 @@ module.exports = {
608
917
  scanCodexSession,
609
918
  buildSessionAnalytics,
610
919
  summarizeSessions,
920
+ listSessionsForBrowser,
921
+ resumeCommandFor,
611
922
  sessionsToCsv,
612
923
  };
@@ -1,2 +0,0 @@
1
- const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/BadgeDetailModal-Bg-AL5Y_.js","assets/__vite-browser-external-CDtmmXRU.js","assets/__vite-browser-external-BEMfOISl.css","assets/react-hAUBl17Q.js","assets/AchievementBadge-XEgOYdAL.js","assets/zap-D2t6DGdL.js","assets/flame-DhLu4C_0.js","assets/use-transform-IS87eWhx.js","assets/use-reduced-motion-_YIc4wth.js","assets/AchievementsSection-DVba9yNb.js","assets/format-tokens-Chz9fmBF.js","assets/DialogTitle-BYRgo5mK.js","assets/useScrollLock-DU8uPOsl.js"])))=>i.map(i=>d[i]);
2
- import{r as l,F as x,G as f,H as v,j as e,c,L as p,R as g,a as y,I as b,J as k,_ as j}from"./__vite-browser-external-CDtmmXRU.js";import{A as w}from"./AchievementsSection-DVba9yNb.js";import{B as h}from"./AchievementBadge-XEgOYdAL.js";import"./react-hAUBl17Q.js";import"./format-tokens-Chz9fmBF.js";import"./use-reduced-motion-_YIc4wth.js";import"./zap-D2t6DGdL.js";import"./flame-DhLu4C_0.js";function N({enabled:a=!0}={}){const[r,t]=l.useState({status:"loading",achievements:[],error:null}),s=l.useMemo(()=>x(),[]);return l.useEffect(()=>{if(!a){t({status:"disabled",achievements:[],error:null});return}let i=!1;return t(n=>({...n,status:"loading"})),f({timeZone:s,tzOffsetMinutes:v()}).then(n=>{i||t({status:"ready",achievements:Array.isArray(n?.achievements)?n.achievements:[],error:null})}).catch(n=>{i||t({status:"error",achievements:[],error:n})}),()=>{i=!0}},[a,s]),r}const A=g.lazy(()=>j(()=>import("./BadgeDetailModal-Bg-AL5Y_.js"),__vite__mapDeps([0,1,2,3,4,5,6,7,8,9,10,11,12])));function I({authLoading:a,authEnabled:r,authUserId:t,mockEnabled:s}){if(s)return{authLoading:!1,signedIn:!0,userId:"mock-user"};const i=!!(r&&t);return{authLoading:!!a,signedIn:i,userId:i?t:null}}function B(){const a=y(),[r,t]=l.useState({status:"loading",achievements:[],userId:null}),s=b(),{authLoading:i,signedIn:n,userId:o}=I({authLoading:a?.loading,authEnabled:a?.enabled,authUserId:a?.user?.id,mockEnabled:s});return l.useEffect(()=>{if(i){t({status:"loading",achievements:[],userId:null});return}if(!n){t({status:"signed-out",achievements:[],userId:null});return}let d=!1;return t({status:"loading",achievements:[],userId:o}),(async()=>{try{const u=s?null:await a.getAccessToken?.(),m=await k({accessToken:u,userId:o});if(d)return;t({status:"ready",achievements:Array.isArray(m?.badges)?m.badges:[],userId:o})}catch{d||t({status:"error",achievements:[],userId:o})}})(),()=>{d=!0}},[i,s,n,o]),{...r,signedIn:n,settled:_({authLoading:i,signedIn:n,userId:o,state:r})}}function _({authLoading:a,signedIn:r,userId:t,state:s}){return a?!1:r?s.userId===t&&(s.status==="ready"||s.status==="error"):s.status==="signed-out"}function S(){return e.jsx("div",{className:"mx-0 grid animate-pulse grid-cols-[repeat(3,max-content)] justify-between gap-y-7 sm:-mx-2 sm:grid-cols-[repeat(4,max-content)] lg:grid-cols-[repeat(5,max-content)]","aria-hidden":!0,children:Array.from({length:h.length}).map((a,r)=>e.jsxs("div",{className:"flex w-[6.75rem] flex-col items-center px-0 pb-3 pt-4 sm:w-[7.75rem]",children:[e.jsx("div",{className:"h-[108px] w-[108px] rounded-full bg-oai-gray-200 dark:bg-oai-gray-800"}),e.jsx("div",{className:"mt-3 h-3 w-16 rounded bg-oai-gray-200 dark:bg-oai-gray-800"}),e.jsx("div",{className:"mt-2 h-2 w-10 rounded bg-oai-gray-100 dark:bg-oai-gray-800/60"})]},r))})}function z(){const a=N(),r=B(),[t,s]=l.useState(null),i=a.status==="loading"||!r.settled,n=l.useMemo(()=>[...r.achievements||[],...a.achievements||[]],[r.achievements,a.achievements]),o=l.useMemo(()=>n.filter(u=>(u?.tier||0)>=1).length,[n]),d=h.length;return e.jsxs("div",{className:"flex flex-1 flex-col font-oai text-oai-black antialiased dark:text-oai-white",children:[e.jsx("main",{className:"flex-1 pb-12 pt-8 sm:pb-16 sm:pt-10",children:e.jsxs("div",{className:"mx-auto max-w-4xl px-4 sm:px-6",children:[e.jsxs("div",{className:"mb-7 flex items-baseline justify-between gap-4",children:[e.jsxs("div",{className:"min-w-0",children:[e.jsx("h1",{className:"text-3xl font-semibold tracking-tight text-oai-black dark:text-white sm:text-4xl",children:c("achievements.page.title")}),e.jsx("p",{className:"mt-1.5 text-sm text-oai-gray-500 dark:text-oai-gray-400",children:c("achievements.page.local_note")})]}),!i&&e.jsxs("div",{className:"shrink-0 text-right",children:[e.jsxs("div",{className:"font-mono text-2xl font-semibold leading-none tabular-nums text-oai-black dark:text-white",children:[o,e.jsxs("span",{className:"text-oai-gray-400 dark:text-oai-gray-600",children:["/",d]})]}),e.jsx("div",{className:"mt-2 h-1 w-24 overflow-hidden rounded-full bg-oai-gray-200 dark:bg-oai-gray-800",children:e.jsx("div",{className:"h-full rounded-full bg-oai-brand-600 transition-[width] duration-500",style:{width:`${Math.round(o/d*100)}%`}})})]})]}),!r.signedIn&&!i&&e.jsxs("div",{className:"mb-6 flex flex-wrap items-center justify-between gap-x-4 gap-y-2 rounded-xl border border-oai-gray-200 bg-oai-gray-50 px-4 py-3 dark:border-oai-gray-800 dark:bg-oai-gray-900/60",children:[e.jsx("p",{className:"text-sm text-oai-gray-600 dark:text-oai-gray-300",children:c("achievements.signin.prompt")}),e.jsx(p,{to:"/login",className:"shrink-0 rounded-lg bg-oai-brand-600 px-3 py-1.5 text-sm font-medium text-white transition-colors duration-200 hover:bg-oai-brand-700 active:scale-[0.98]",children:c("achievements.signin.action")})]}),i?e.jsx(S,{}):e.jsxs(e.Fragment,{children:[e.jsx(w,{achievements:n,isOwn:!0,columns:"wide",size:"lg",animateIn:!0,onSelect:s}),e.jsxs("p",{className:"mt-10 text-center text-xs text-oai-gray-400 dark:text-oai-gray-500",children:[c("achievements.page.suggest")," ",e.jsx("a",{href:"https://github.com/mm7894215/TokenTracker/issues",target:"_blank",rel:"noopener noreferrer",className:"underline decoration-oai-gray-300 underline-offset-2 transition-colors hover:text-oai-gray-700 hover:decoration-oai-gray-500 dark:decoration-oai-gray-600 dark:hover:text-oai-gray-300",children:c("achievements.page.suggest_link")})]})]})]})}),t&&e.jsx(g.Suspense,{fallback:null,children:e.jsx(A,{badge:t,onClose:()=>s(null)})})]})}export{_ as cloudBadgesSettled,z as default,I as resolveCloudBadgeIdentity};
@@ -1 +0,0 @@
1
- import{r as L,j as a,bb as De,c as h,d as ye,X as Ie}from"./__vite-browser-external-CDtmmXRU.js";import{k as _e,V as Me,l as Ve,r as Te,n as $e,o as be,q as ve,s as Oe,t as Re,v as Be,w as Pe,x as Ke,y as Ce,z as Fe,A as Le,B as Xe,S as Ge,H as We,C as G,D as ze,E as Ue,e as Ye,F as He,m as C}from"./react-hAUBl17Q.js";import{c as je,A as Ze,p as qe,b as U,t as le,a as T,d as Je}from"./AchievementBadge-XEgOYdAL.js";import{u as Qe,a as z}from"./use-transform-IS87eWhx.js";import{u as et}from"./use-reduced-motion-_YIc4wth.js";import{l as ce,f as ue,b as tt}from"./AchievementsSection-DVba9yNb.js";import{D as nt,a as rt,b as at,c as st,d as it,e as ot}from"./DialogTitle-BYRgo5mK.js";import"./zap-D2t6DGdL.js";import"./flame-DhLu4C_0.js";import"./format-tokens-Chz9fmBF.js";import"./useScrollLock-DU8uPOsl.js";const lt=(e,t,n)=>{const r=t-e;return((n-e)%r+r)%r+e};function Ne(e,t){return _e(e)?e[lt(0,e.length,t)]:e}class ct{constructor(t){this.stop=()=>this.runAll("stop"),this.animations=t.filter(Boolean)}get finished(){return Promise.all(this.animations.map(t=>t.finished))}getAll(t){return this.animations[0][t]}setAll(t,n){for(let r=0;r<this.animations.length;r++)this.animations[r][t]=n}attachTimeline(t){const n=this.animations.map(r=>r.attachTimeline(t));return()=>{n.forEach((r,s)=>{r&&r(),this.animations[s].stop()})}}get time(){return this.getAll("time")}set time(t){this.setAll("time",t)}get speed(){return this.getAll("speed")}set speed(t){this.setAll("speed",t)}get state(){return this.getAll("state")}get startTime(){return this.getAll("startTime")}get duration(){return de(this.animations,"duration")}get iterationDuration(){return de(this.animations,"iterationDuration")}runAll(t){this.animations.forEach(n=>n[t]())}play(){this.runAll("play")}pause(){this.runAll("pause")}cancel(){this.runAll("cancel")}complete(){this.runAll("complete")}}function de(e,t){let n=0;for(let r=0;r<e.length;r++){const s=e[r][t];s!==null&&s>n&&(n=s)}return n}class ut extends ct{then(t,n){return this.finished.finally(t).then(()=>{})}}function dt(e,t){return e in t}class mt extends Me{constructor(){super(...arguments),this.type="object"}readValueFromInstance(t,n){if(dt(n,t)){const r=t[n];if(typeof r=="string"||typeof r=="number")return r}}getBaseTargetFromProps(){}removeValueFromRenderState(t,n){delete n.output[t]}measureInstanceViewportBox(){return Ve()}build(t,n){Object.assign(t.output,n)}renderInstance(t,{output:n}){Object.assign(t,n)}sortInstanceNodePosition(){return 0}}function Y(e){return typeof e=="object"&&!Array.isArray(e)}function ke(e,t,n,r){return e==null?[]:typeof e=="string"&&Y(t)?Te(e,n,r):e instanceof NodeList?Array.from(e):Array.isArray(e)?e.filter(s=>s!=null):[e]}function ft(e,t,n){return e*(t+1)+n*t}function me(e,t,n,r){return typeof t=="number"?t:t.startsWith("-")||t.startsWith("+")?Math.max(0,e+parseFloat(t)):t==="<"?n:t.startsWith("<")?Math.max(0,n+parseFloat(t.slice(1))):r.get(t)??e}function pt(e,t,n){for(let r=0;r<e.length;r++){const s=e[r];s.at>t&&s.at<n&&(be(e,s),r--)}}function ht(e,t,n,r,s,c){pt(e,s,c);for(let i=0;i<t.length;i++)e.push({value:t[i],at:$e(s,c,r[i]),easing:Ne(n,i)})}function gt(e,t,n=0){const r=t+1+t*n;for(let s=0;s<e.length;s++)e[s]=e[s]/r}function xt(e,t){return e.at===t.at?e.value===null?1:t.value===null?-1:0:e.at-t.at}const yt="easeInOut",bt=20;function vt(e,{defaultTransition:t={},...n}={},r,s){const c=t.duration||.3,i=new Map,f=new Map,m={},p=new Map;let g=0,u=0,x=0;for(let N=0;N<e.length;N++){const k=e[N];if(typeof k=="string"){p.set(k,u);continue}else if(!Array.isArray(k)){p.set(k.name,me(u,k.at,g,p));continue}let[E,b,v={}]=k;v.at!==void 0&&(u=me(u,v.at,g,p));let j=0;const _=(w,M,A,o=0,l=0)=>{const d=jt(w),{delay:D=0,times:y=Oe(d),type:R=t.type||"keyframes",repeat:B,repeatType:W,repeatDelay:H=0,...Se}=M;let{ease:I=t.ease||"easeOut",duration:S}=M;const Z=typeof D=="function"?D(o,l):D,q=d.length,J=Ke(R)?R:s?.[R||"keyframes"];if(q<=2&&J){let $=100;if(q===2&&wt(d)){const P=d[1]-d[0];$=Math.abs(P)}const O={...t,...Se};S!==void 0&&(O.duration=Ce(S));const X=Re(O,$,J);I=X.ease,S=X.duration}S??(S=c);const Q=u+Z;y.length===1&&y[0]===0&&(y[1]=1);const ee=y.length-d.length;if(ee>0&&Be(y,ee),d.length===1&&d.unshift(null),B&&B<bt){const $=S>0?H/S:0;S=ft(S,B,H);const O=[...d],X=[...y];I=Array.isArray(I)?[...I]:[I];const P=[...I],ne=W==="reverse"||W==="mirror";let re=O,ae=P;ne&&(re=[...O].reverse(),W==="reverse"&&(ae=[...P].reverse().map(V=>typeof V=="function"?Pe(V):V)));for(let V=0;V<B;V++){const se=ne&&V%2===0,ie=se?re:O,Ee=se?ae:P,oe=(V+1)*(1+$);$>0&&(d.push(d[d.length-1]),y.push(oe),I.push("linear")),d.push(...ie);for(let K=0;K<ie.length;K++)y.push(X[K]+oe),I.push(K===0?"linear":Ne(Ee,K-1))}gt(y,B,$)}const te=Q+S;ht(A,d,I,y,Q,te),j=Math.max(Z+S,j),x=Math.max(te,x)};if(ve(E)){const w=fe(E,f);_(b,v,pe("default",w))}else{const w=ke(E,b,r,m),M=w.length;for(let A=0;A<M;A++){b=b,v=v;const o=w[A],l=fe(o,f);for(const d in b)_(b[d],Nt(v,d),pe(d,l),A,M)}}g=u,u+=j}return f.forEach((N,k)=>{for(const E in N){const b=N[E];b.sort(xt);const v=[],j=[],_=[];for(let o=0;o<b.length;o++){const{at:l,value:d,easing:D}=b[o];v.push(d),j.push(Fe(0,x,l)),_.push(D||"easeOut")}j[0]!==0&&(j.unshift(0),v.unshift(v[0]),_.unshift(yt)),j[j.length-1]!==1&&(j.push(1),v.push(null)),i.has(k)||i.set(k,{keyframes:{},transition:{}});const w=i.get(k);w.keyframes[E]=v;const{type:M,...A}=t;w.transition[E]={...A,duration:x,ease:_,times:j,...n}}}),i}function fe(e,t){return!t.has(e)&&t.set(e,{}),t.get(e)}function pe(e,t){return t[e]||(t[e]=[]),t[e]}function jt(e){return Array.isArray(e)?e:[e]}function Nt(e,t){return e&&e[t]?{...e,...e[t]}:{...e}}const kt=e=>typeof e=="number",wt=e=>e.every(kt);function At(e){const t={presenceContext:null,props:{},visualState:{renderState:{transform:{},transformOrigin:{},style:{},vars:{},attrs:{}},latestValues:{}}},n=Le(e)&&!Xe(e)?new Ge(t):new We(t);n.mount(e),G.set(e,n)}function St(e){const t={presenceContext:null,props:{},visualState:{renderState:{output:{}},latestValues:{}}},n=new mt(t);n.mount(e),G.set(e,n)}function Et(e,t){return ve(e)||typeof e=="number"||typeof e=="string"&&!Y(t)}function we(e,t,n,r){const s=[];if(Et(e,t))s.push(ze(e,Y(t)&&t.default||t,n&&(n.default||n)));else{if(e==null)return s;const c=ke(e,t,r),i=c.length;for(let f=0;f<i;f++){const m=c[f],p=m instanceof Element?At:St;G.has(m)||p(m);const g=G.get(m),u={...n};"delay"in u&&typeof u.delay=="function"&&(u.delay=u.delay(f,i)),s.push(...Ue(g,{...t,transition:u},{}))}}return s}function Dt(e,t,n){const r=[],s=e.map(i=>{if(Array.isArray(i)&&typeof i[0]=="function"){const f=i[0],m=Ye(0);return m.on("change",f),i.length===1?[m,[0,1]]:i.length===2?[m,[0,1],i[1]]:[m,i[1],i[2]]}return i});return vt(s,t,n,{spring:He}).forEach(({keyframes:i,transition:f},m)=>{r.push(...we(m,i,f))}),r}function It(e){return Array.isArray(e)&&e.some(Array.isArray)}function _t(e={}){const{scope:t,reduceMotion:n,skipAnimations:r}=e;function s(c,i,f){let m=[],p;const g={};if(n!==void 0&&(g.reduceMotion=n),r!==void 0&&(g.skipAnimations=r),It(c)){const{onComplete:x,...N}=i||{};typeof x=="function"&&(p=x),m=Dt(c,{...g,...N},t)}else{const{onComplete:x,...N}=f||{};typeof x=="function"&&(p=x),m=we(c,i,{...g,...N},t)}const u=new ut(m);return p&&u.finished.then(p),t&&(t.animations.push(u),u.finished.then(()=>{be(t.animations,u)})),u}return s}const F=_t(),he=8;function Mt({badgeId:e,tier:t,locked:n=!1,size:r=176,backLines:s=[],label:c}){const i=Qe(0),f=et(),m=L.useRef(null),p=L.useRef(null),g=L.useRef(!1),u=qe(t,n),x=je.get(e)?.icon||null,N=z(i,o=>`${(o%360+360)%360/360*160-80}%`),k=z(i,o=>`${80-(o%360+360)%360/360*160}%`),E=z(i,o=>{const l=Math.abs(Math.sin(o*Math.PI/180));return Math.max(0,(l-.35)/.65)*.3}),b=()=>{p.current?.stop?.(),p.current=null};L.useEffect(()=>{if(f||g.current)return;let o=!1;i.set(-360);const l=F(i,0,{duration:.9,ease:[.16,1,.3,1]});return p.current=l,l.then(()=>{o||g.current||(p.current=F(i,[0,-14,14,0],{duration:4.5,repeat:1/0,ease:"easeInOut"}))}),()=>{o=!0,b()}},[f,i]);const v=o=>{const l=i.get();if(f){const y=Math.round(l/180)*180;p.current=F(i,y,{duration:.2,ease:"easeOut"});return}const d=l+o*.35,D=Math.round(d/180)*180;p.current=F(i,D,{type:"spring",velocity:o,stiffness:58,damping:13.5,mass:1})},j=o=>{g.current=!0,b(),o.currentTarget.setPointerCapture?.(o.pointerId),m.current={pointerId:o.pointerId,startX:o.clientX,startRot:i.get(),samples:[{t:o.timeStamp,x:o.clientX}]}},_=o=>{const l=m.current;!l||l.pointerId!==o.pointerId||(i.set(l.startRot+(o.clientX-l.startX)*.55),l.samples.push({t:o.timeStamp,x:o.clientX}),l.samples.length>5&&l.samples.shift())},w=o=>{const l=m.current;if(!l||l.pointerId!==o.pointerId)return;m.current=null;const d=l.samples[0],D=l.samples[l.samples.length-1],y=D.t-d.t,R=y>0?(D.x-d.x)*.55*1e3/y:0;v(R)},M=o=>{if(o.key!=="ArrowLeft"&&o.key!=="ArrowRight")return;o.preventDefault(),g.current=!0,b();const l=o.key==="ArrowRight"?30:-30;p.current=F(i,i.get()+l,{duration:.15,ease:"easeOut"})},A={backfaceVisibility:"hidden",WebkitBackfaceVisibility:"hidden"};return a.jsx("div",{role:"img","aria-label":c,tabIndex:0,onPointerDown:j,onPointerMove:_,onPointerUp:w,onPointerCancel:w,onKeyDown:M,className:"inline-flex cursor-grab select-none items-center justify-center outline-none focus-visible:ring-2 focus-visible:ring-oai-brand/60 rounded-full active:cursor-grabbing",style:{perspective:900,touchAction:"pan-y"},children:a.jsxs("div",{className:"relative",style:{transform:"rotateX(-8deg)",transformStyle:"preserve-3d"},children:[a.jsxs(C.div,{className:"relative",style:{rotateY:i,transformStyle:"preserve-3d",width:r,height:r},children:[[-3,-1.5,0,1.5,3].map(o=>a.jsx("div",{"aria-hidden":!0,className:"absolute inset-0 rounded-full",style:{transform:`translateZ(${o}px)`,background:u.rim[1]}},o)),a.jsxs("div",{className:"absolute inset-0 overflow-hidden rounded-full",style:{...A,transform:`translateZ(${he/2}px)`},children:[a.jsx(Ze,{badgeId:e,tier:t,locked:n,size:"fill",className:"brightness-105 saturate-[1.08]"}),a.jsx("div",{"aria-hidden":!0,className:"pointer-events-none absolute inset-0 rounded-full",style:{background:"radial-gradient(circle at 30% 20%, rgba(255,255,255,0.45), rgba(255,255,255,0) 42%), linear-gradient(172deg, rgba(255,255,255,0.14) 0%, rgba(255,255,255,0) 38%, rgba(0,0,0,0.08) 100%)",boxShadow:"inset 0 3px 7px rgba(255,255,255,0.5), inset 0 -3px 8px rgba(0,0,0,0.15)"}}),a.jsx(C.div,{"aria-hidden":!0,className:"pointer-events-none absolute inset-0",style:{x:N,background:"linear-gradient(105deg, transparent 34%, rgba(255,255,255,0.5) 50%, transparent 66%)"}}),a.jsx(C.div,{"aria-hidden":!0,className:"pointer-events-none absolute inset-0 rounded-full",style:{opacity:E,background:"rgba(8, 12, 10, 0.9)"}})]}),a.jsxs("div",{className:"absolute inset-0 flex flex-col items-center justify-center gap-1 overflow-hidden rounded-full px-6 text-center",style:{...A,transform:`rotateY(180deg) translateZ(${he/2}px)`,background:`linear-gradient(145deg, ${u.face[0]}, ${u.face[1]})`,boxShadow:`inset 0 0 0 6px ${u.rim[1]}, inset 0 0 0 7.5px rgba(255,255,255,0.22), inset 0 3px 7px rgba(255,255,255,0.4), inset 0 -4px 12px rgba(0,0,0,0.22)`},children:[a.jsx("div",{"aria-hidden":!0,className:"pointer-events-none absolute inset-0 rounded-full",style:{background:"repeating-radial-gradient(circle at 50% 50%, rgba(255,255,255,0.06) 0px, rgba(255,255,255,0.06) 1px, transparent 1px, transparent 4px)"}}),x?a.jsx(x,{"aria-hidden":!0,size:Math.round(r*.62),strokeWidth:1.1,color:u.glyph,className:"pointer-events-none absolute opacity-[0.13]",style:{filter:"drop-shadow(0 1px 0 rgba(255,255,255,0.3)) drop-shadow(0 -1px 0 rgba(0,0,0,0.35))"}}):null,a.jsx("div",{"aria-hidden":!0,className:"pointer-events-none absolute inset-0 rounded-full",style:{background:"radial-gradient(circle at 70% 20%, rgba(255,255,255,0.4), rgba(255,255,255,0) 44%), linear-gradient(188deg, rgba(255,255,255,0.12) 0%, rgba(255,255,255,0) 38%, rgba(0,0,0,0.10) 100%)"}}),s.map((o,l)=>a.jsx("div",{className:`font-mono tabular-nums ${l===0?"text-[10px] uppercase tracking-[0.18em]":l===1?"text-lg font-bold":"text-[11px]"}`,style:{color:u.glyph,textShadow:"0 1px 0 rgba(255,255,255,0.28), 0 -1px 0 rgba(0,0,0,0.3)"},children:o},`${l}-${o}`)),a.jsx(C.div,{"aria-hidden":!0,className:"pointer-events-none absolute inset-0",style:{x:k,background:"linear-gradient(105deg, transparent 34%, rgba(255,255,255,0.45) 50%, transparent 66%)"}}),a.jsx(C.div,{"aria-hidden":!0,className:"pointer-events-none absolute inset-0 rounded-full",style:{opacity:E,background:"rgba(8, 12, 10, 0.9)"}})]})]}),a.jsx("div",{"aria-hidden":!0,className:"absolute left-1/2 h-3 w-3/5 rounded-[100%] bg-black/20 blur-md dark:bg-black/45",style:{bottom:-20,transform:"translateX(-50%)"}})]})})}function Ae(e){if(!e)return null;const t=Date.parse(e);return Number.isFinite(t)?new Date(t).toLocaleDateString():null}function Vt(e){const t=Number(e);return!Number.isSafeInteger(t)||t<1?null:String(t).padStart(6,"0")}function Tt(e,t,n){if(t?.scope!=="cloud"||!n)return null;const r=Number(e?.serials?.[n]);return Number.isSafeInteger(r)&&r>=1?r:null}function $t({badge:e}){return a.jsx("div",{className:"flex items-center gap-1",children:Je.map((t,n)=>{const r=(e.tier||0)>=n+1,s=Ae(e.achieved?.[t]),c=T[t];return a.jsx("span",{title:s?`${h(`achievements.tier.${t}`)} · ${s}`:h(`achievements.tier.${t}`),className:"inline-flex h-2 w-8 rounded-full",style:r?{background:`linear-gradient(120deg, ${c.rim[0]} 0%, ${c.ring} 55%, ${c.rim[1]} 100%)`,boxShadow:"inset 0 1px 1px rgba(255,255,255,0.4), 0 0 0 1px rgba(10,15,12,0.05)"}:void 0,children:!r&&a.jsx("span",{className:"h-full w-full rounded-full bg-oai-gray-200 dark:bg-oai-gray-800"})},t)})})}function ge({badge:e,onClose:t}){const n=je.get(e.id),r=h(U(e.id,"name")),s=(e.tier||0)>=1,c=s?le(e.tier):null,i=Ae(s?e.achieved?.[c]||(ce(e)?new Date(ce(e)).toISOString():null):null),f=ue(n?.format,e.metric_value),m=tt(e),p=e.tier<4?le((e.tier||0)+1):null,g=Tt(e,n,c),u=Vt(g),x=u?[h("achievements.modal.achieved_label"),h("achievements.modal.issue_number",{number:u}),i||r]:[s?h("achievements.modal.achieved_label"):h("achievements.modal.locked"),s?f:"···",s&&i?i:r];return a.jsxs("div",{className:"flex flex-col items-center px-6 pb-6 pt-5 text-center",children:[a.jsxs("div",{className:"flex w-full items-start justify-between gap-3",children:[a.jsxs("div",{className:"min-w-0 flex-1 text-left",children:[a.jsx("h2",{className:"truncate text-lg font-semibold tracking-tight text-oai-black dark:text-white",children:r}),a.jsx("p",{className:"mt-0.5 text-xs text-oai-gray-500 dark:text-oai-gray-400",children:h(U(e.id,"desc"))})]}),a.jsx("button",{type:"button",onClick:t,"aria-label":h("achievements.modal.close"),className:"shrink-0 rounded-md p-1.5 text-oai-gray-400 transition-colors hover:bg-oai-gray-100 hover:text-oai-gray-700 dark:hover:bg-oai-gray-900 dark:hover:text-oai-gray-200",children:a.jsx(Ie,{size:16})})]}),a.jsx("div",{className:"mt-5 mb-7",children:a.jsx(Mt,{badgeId:e.id,tier:e.tier,locked:!s,backLines:x,label:h("achievements.modal.coin_aria",{name:r})})}),a.jsxs("div",{className:"flex items-center gap-2",children:[a.jsx("span",{className:"rounded-full px-2.5 py-0.5 text-[11px] font-semibold uppercase tracking-wide",style:s?{background:`linear-gradient(120deg, ${T[c].rim[0]} 0%, ${T[c].ring} 55%, ${T[c].rim[1]} 100%)`,color:T[c].glyph,textShadow:"0 1px 0 rgba(255,255,255,0.35)",boxShadow:"inset 0 1px 1px rgba(255,255,255,0.4), 0 0 0 1px rgba(10,15,12,0.06)"}:{background:"rgba(128,134,130,0.75)",color:"rgba(250,250,250,0.95)"},children:s?h(`achievements.tier.${c}`):h("achievements.modal.locked")}),s&&i?a.jsx("span",{className:"text-xs tabular-nums text-oai-gray-500 dark:text-oai-gray-400",children:i}):null]}),a.jsxs("dl",{className:"mt-5 w-full space-y-3 border-t border-oai-gray-200/70 pt-4 text-left dark:border-oai-gray-800/60",children:[a.jsxs("div",{className:"flex items-baseline justify-between gap-3 text-sm",children:[a.jsx("dt",{className:"text-oai-gray-500 dark:text-oai-gray-400",children:h("achievements.modal.value_label")}),a.jsx("dd",{className:"font-mono font-semibold tabular-nums text-oai-black dark:text-white",children:f})]}),a.jsxs("div",{children:[a.jsxs("div",{className:"flex items-baseline justify-between gap-3 text-xs text-oai-gray-500 dark:text-oai-gray-400",children:[a.jsx("span",{children:e.tier>=4?h("achievements.modal.max_tier"):h("achievements.modal.progress_to_next",{tier:h(`achievements.tier.${p}`)})}),e.tier<4&&e.next_threshold!=null?a.jsxs("span",{className:"font-mono tabular-nums",children:[f," ",e.lower_is_better?"→":"/"," ",ue(n?.format,e.next_threshold)]}):null]}),a.jsx("div",{className:"mt-1.5 h-1.5 w-full overflow-hidden rounded-full bg-oai-gray-200 dark:bg-oai-gray-800",children:a.jsx("div",{className:"h-full rounded-full transition-[width] duration-500",style:{width:`${Math.round((e.tier>=4?1:m)*100)}%`,background:s?`linear-gradient(90deg, ${T[c].rim[0]} 0%, ${T[c].ring} 55%, ${T[c].rim[1]} 100%)`:"rgba(128,134,130,0.75)",boxShadow:s?"inset 0 1px 1px rgba(255,255,255,0.35)":void 0}})})]}),a.jsxs("div",{className:"flex items-center justify-between gap-3 text-xs text-oai-gray-500 dark:text-oai-gray-400",children:[a.jsx("span",{children:h("achievements.modal.tier_history")}),a.jsx($t,{badge:e})]})]})]})}const xe=ye("relative w-full max-w-[380px] flex flex-col","rounded-2xl bg-white dark:bg-oai-gray-950","shadow-[0_20px_60px_-20px_rgba(0,0,0,0.25)] dark:shadow-[0_20px_60px_-10px_rgba(0,0,0,0.65)]","ring-1 ring-oai-gray-200 dark:ring-oai-gray-800 overflow-hidden","dark:border-t dark:border-white/[0.08]");function zt({badge:e,onClose:t}){const n=De(),r=!!e;return L.useEffect(()=>{if(!n||!r)return;const s=c=>{c.key==="Escape"&&(c.stopPropagation(),t?.())};return window.addEventListener("keydown",s,!0),()=>window.removeEventListener("keydown",s,!0)},[n,r,t]),r?n?a.jsxs("div",{className:"fixed inset-0 z-[120] flex items-center justify-center p-4",children:[a.jsx("div",{className:"absolute inset-0 bg-black/40 backdrop-blur-[2px]",onClick:t,"aria-hidden":!0}),a.jsx("div",{className:xe,children:a.jsx(ge,{badge:e,onClose:t})})]}):a.jsx(nt,{open:!0,onOpenChange:s=>{s||t?.()},children:a.jsxs(rt,{children:[a.jsx(at,{className:"cost-modal-backdrop z-[110]"}),a.jsx(st,{className:"fixed inset-0 z-[120] flex items-center justify-center p-4",children:a.jsxs(it,{className:ye("cost-modal-popup",xe),children:[a.jsx(ot,{render:a.jsx("h2",{className:"sr-only"}),children:h(U(e.id,"name"))}),a.jsx(ge,{badge:e,onClose:t})]})})]})}):null}export{zt as BadgeDetailModal,Tt as currentBadgeIssueNumber,zt as default,Vt as formatIssueNumber};
@@ -1 +0,0 @@
1
- import{l as E,e as M,r,E as D,c as i,j as o,d as I}from"./__vite-browser-external-CDtmmXRU.js";import{m as j}from"./react-hAUBl17Q.js";import{P as L}from"./ProviderIcon-Dd7G999Z.js";import{g as A,S as $}from"./skills-api-DSMG8EyE.js";import{u as R}from"./use-reduced-motion-_YIc4wth.js";import{A as K}from"./index-B6zXwIZo.js";const q=[["path",{d:"M20 4v7a4 4 0 0 1-4 4H4",key:"6o5b7l"}],["path",{d:"m9 10-5 5 5 5",key:"1kshq7"}]],O=E("corner-down-left",q),T=typeof window<"u"&&(window.location.hostname==="localhost"||window.location.hostname==="127.0.0.1");function G(){const f=M(),d=R(),[u,g]=r.useState(!1),[x,h]=r.useState(""),[n,l]=r.useState(0),[p,k]=r.useState([]),b=r.useRef(null),w=r.useRef(null);r.useEffect(()=>{const e=t=>{(t.metaKey||t.ctrlKey)&&(t.key==="k"||t.key==="K")&&(t.preventDefault(),g(c=>!c))};return window.addEventListener("keydown",e),()=>window.removeEventListener("keydown",e)},[]),r.useEffect(()=>{if(!u)return;h(""),l(0);const e=requestAnimationFrame(()=>b.current?.focus());return T&&!p.length&&A().then(t=>k(t?.skills||[])).catch(()=>k([])),()=>cancelAnimationFrame(e)},[u,p.length]);const y=r.useMemo(()=>{const e=D().flatMap(a=>a.items.map(c=>({kind:"page",id:`page:${c.id}`,label:c.label,sub:i("cmdk.group.pages"),Icon:c.icon,run:()=>f(c.to)}))),t=(p||[]).map(a=>({kind:"skill",id:`skill:${a.id||a.directory}`,label:a.name||a.directory,sub:a.repoOwner&&a.repoName?`${a.repoOwner}/${a.repoName}`:i("cmdk.group.skills"),provider:(a.targets||[])[0]||"claude",run:()=>f(`/skills?skill=${encodeURIComponent(a.directory||a.id)}`)}));return[...e,...t]},[f,p]),s=r.useMemo(()=>{const e=x.trim().toLowerCase();return e?y.filter(t=>t.label.toLowerCase().includes(e)||String(t.sub||"").toLowerCase().includes(e)):y},[y,x]);r.useEffect(()=>{n>=s.length&&l(s.length?s.length-1:0)},[n,s.length]),r.useEffect(()=>{if(!u)return;w.current?.querySelector(`[data-cmdk-index="${n}"]`)?.scrollIntoView({block:"nearest"})},[n,u]);const m=r.useCallback(()=>g(!1),[]),v=r.useCallback(e=>{e&&(m(),e.run())},[m]),N=e=>{e.key==="ArrowDown"?(e.preventDefault(),l(t=>Math.min(t+1,Math.max(0,s.length-1)))):e.key==="ArrowUp"?(e.preventDefault(),l(t=>Math.max(t-1,0))):e.key==="Enter"?(e.preventDefault(),v(s[n])):e.key==="Escape"&&(e.preventDefault(),m())},C={duration:d?0:.12},S=d?{duration:0}:{type:"spring",stiffness:460,damping:34,mass:.6};return o.jsx(K,{children:u?o.jsxs(j.div,{className:"fixed inset-0 z-[120] flex items-start justify-center px-4 pt-[16vh]",initial:d?!1:{opacity:0},animate:{opacity:1},exit:{opacity:0},transition:C,children:[o.jsx("button",{type:"button","aria-label":i("cmdk.close"),onClick:m,className:"absolute inset-0 cursor-default bg-oai-black/40"}),o.jsxs(j.div,{role:"dialog","aria-modal":"true","aria-label":i("cmdk.open_aria"),initial:d?!1:{opacity:0,y:-8,scale:.985},animate:{opacity:1,y:0,scale:1},exit:d?{opacity:0}:{opacity:0,y:-8,scale:.985},transition:S,className:"relative w-full max-w-xl overflow-hidden rounded-2xl border border-oai-gray-200 bg-oai-white shadow-2xl dark:border-oai-gray-800 dark:bg-oai-gray-950",children:[o.jsxs("div",{className:"flex items-center gap-2.5 border-b border-oai-gray-200 px-4 dark:border-oai-gray-800",children:[o.jsx($,{className:"h-4 w-4 shrink-0 text-oai-gray-400","aria-hidden":!0}),o.jsx("input",{ref:b,type:"text",value:x,onChange:e=>{h(e.target.value),l(0)},onKeyDown:N,"aria-label":i("cmdk.placeholder"),"aria-controls":"cmdk-listbox","aria-activedescendant":s[n]?`cmdk-opt-${n}`:void 0,role:"combobox","aria-expanded":"true",placeholder:i("cmdk.placeholder"),className:"h-12 w-full bg-transparent text-sm text-oai-black placeholder:text-oai-gray-400 focus:outline-none dark:text-white"})]}),o.jsx("div",{ref:w,id:"cmdk-listbox",role:"listbox",className:"max-h-[min(56vh,22rem)] overflow-y-auto p-1.5",children:s.length===0?o.jsx("div",{className:"px-3 py-8 text-center text-sm text-oai-gray-500 dark:text-oai-gray-400",children:i("cmdk.empty")}):s.map((e,t)=>{const a=t===n;return o.jsxs("button",{type:"button",role:"option",id:`cmdk-opt-${t}`,"data-cmdk-index":t,"aria-selected":a,onMouseMove:()=>l(t),onClick:()=>v(e),className:I("flex w-full items-center gap-3 rounded-lg px-3 py-2 text-left text-sm transition-colors",a?"bg-oai-gray-100 text-oai-black dark:bg-oai-gray-800/70 dark:text-white":"text-oai-gray-700 dark:text-oai-gray-200"),children:[o.jsx("span",{className:"flex h-5 w-5 shrink-0 items-center justify-center text-oai-gray-500 dark:text-oai-gray-400",children:e.kind==="skill"?o.jsx(L,{provider:e.provider,size:16}):o.jsx(e.Icon,{className:"h-4 w-4","aria-hidden":!0})}),o.jsx("span",{className:"min-w-0 flex-1 truncate font-medium",children:e.label}),o.jsx("span",{className:"shrink-0 truncate text-xs text-oai-gray-400 dark:text-oai-gray-500",children:e.sub}),a?o.jsx(O,{className:"h-3.5 w-3.5 shrink-0 text-oai-gray-400","aria-hidden":!0}):null]},e.id)})})]})]}):null})}export{G as CommandPalette};
@@ -1 +0,0 @@
1
- import{l as L,j as e,U as R,m as P,r as D,c as n,X as z,k as S}from"./__vite-browser-external-CDtmmXRU.js";import{m as M}from"./react-hAUBl17Q.js";import{A as K,W as U}from"./icons-BSHx86zo.js";import{u as B}from"./useTokenFormat-BJw_Glux.js";import{P as F}from"./ProviderIcon-Dd7G999Z.js";import{T as G}from"./TrendMonitor-CRxumE5w.js";import{T as W}from"./AchievementBadge-XEgOYdAL.js";import{T as V}from"./terminal-BP0gsSiR.js";import{D as X,a as Y,b as Z,c as H,d as q,e as J}from"./DialogTitle-BYRgo5mK.js";import{D as Q}from"./DialogDescription-B6gmUT6p.js";import{D as ee}from"./DialogClose-Cufbk4wh.js";import"./useCurrency-CzLaB8v2.js";import"./PopoverPopup-Dq4uabm_.js";import"./useScrollLock-DU8uPOsl.js";import"./SelectItemText-DBPZbDEO.js";import"./use-reduced-motion-_YIc4wth.js";import"./zap-D2t6DGdL.js";import"./flame-DhLu4C_0.js";const ae=[["path",{d:"M16 17h6v-6",key:"t6n2it"}],["path",{d:"m22 17-8.5-8.5-5 5L2 7",key:"x473p"}]],te=L("trending-down",ae),O=10,_={input:"var(--community-token-input)",cache_read:"var(--community-token-cache-read)",cache_write:"var(--community-token-cache-write)",output:"var(--community-token-output)",reasoning:"var(--community-token-reasoning)"},re={CLAUDE:"var(--community-claude)",CODEX:"var(--community-codex)",GEMINI:"var(--community-gemini)",CURSOR:"var(--community-cursor)",OPENCODE:"var(--community-opencode)",HERMES:"var(--community-hermes)",KIMI:"var(--community-kimi)",MIMO:"var(--community-mimo)",COPILOT:"var(--community-copilot)",KIRO:"var(--community-kiro)",GROK:"var(--community-grok)",DEEPSEEK:"var(--community-deepseek)",ZCODE:"var(--community-zcode)",WORKBUDDY:"var(--community-workbuddy)",MINIMAX:"var(--community-minimax)",DROID:"var(--community-droid)"};function ie(t){return re[String(t||"").toUpperCase()]||"var(--community-muted)"}const se=[[/claude|fable|opus|sonnet|haiku/,"CLAUDE"],[/gpt|codex|o3|o4/,"CODEX"],[/gemini/,"GEMINI"],[/composer|cursor/,"CURSOR"],[/kimi/,"KIMI"],[/mimo/,"MIMO"],[/copilot/,"COPILOT"],[/kiro/,"KIRO"],[/grok/,"GROK"],[/deepseek/,"DEEPSEEK"],[/glm/,"ZCODE"],[/hy3/,"WORKBUDDY"],[/minimax/,"MINIMAX"]],g=["overview","providers","community","models"];function oe(t){const s=String(t||"").toLowerCase(),a=se.find(([i])=>i.test(s));return a?a[1]:""}function u(t){const s=Number(t);return Number.isFinite(s)?`${s.toFixed(1)}%`:"—"}function p(t){return S(Number(t)||0)}function ne(t){return t?new Date(`${t}T00:00:00Z`).toLocaleDateString(void 0,{month:"short",day:"numeric",timeZone:"UTC"}):""}function k(){return e.jsx("div",{className:"flex h-full min-h-40 items-center justify-center text-xs text-oai-gray-400",children:n("leaderboard.community.modal.no_data")})}function b({children:t,detail:s}){return e.jsxs("div",{className:"mb-3 flex items-baseline justify-between gap-4",children:[e.jsx("h3",{className:"text-xs font-medium uppercase tracking-wide text-oai-gray-500 dark:text-oai-gray-400",children:t}),s?e.jsx("span",{className:"text-xs text-oai-gray-400",children:s}):null]})}function C({value:t,label:s}){const a=Number(t);if(!Number.isFinite(a))return null;const i=Math.sign(a)!==-1,r=i?W:te;return e.jsxs("span",{className:`inline-flex items-center gap-1 text-xs font-medium tabular-nums ${i?"text-emerald-600 dark:text-emerald-400":"text-rose-600 dark:text-rose-400"}`,title:s,children:[e.jsx(r,{className:"size-3","aria-hidden":"true"}),i?"+":"",a.toFixed(1),"%"]})}function j({label:t,value:s,title:a,delta:i,deltaLabel:r}){return e.jsxs("div",{className:"min-w-0",children:[e.jsx("div",{className:"truncate text-xs font-medium text-oai-gray-500 dark:text-oai-gray-400",children:t}),e.jsxs("div",{className:"mt-1 flex min-w-0 items-baseline gap-2",children:[e.jsx("span",{className:"truncate text-xl font-semibold leading-none tracking-tight text-oai-black tabular-nums dark:text-white",title:a,children:s}),e.jsx(C,{value:i,label:r})]})]})}function I({fillWidth:t,share:s,color:a="var(--oai-blue)",ariaLabel:i,dataAttrs:r,children:o}){return e.jsxs("div",{...r,className:"group relative grid min-h-11 grid-cols-[minmax(0,1fr)_auto] items-center gap-3 overflow-hidden px-2.5 transition-colors hover:bg-oai-gray-50 dark:hover:bg-oai-gray-900/60",children:[e.jsx("div",{className:"pointer-events-none absolute inset-y-0 left-0 opacity-[0.15] transition-opacity group-hover:opacity-[0.22] dark:opacity-[0.24] dark:group-hover:opacity-[0.32]",style:{width:`${t}%`,backgroundColor:a},role:"progressbar","aria-label":i,"aria-valuemin":"0","aria-valuemax":"100","aria-valuenow":Number(s)||0}),o]})}function A({rank:t,provider:s,name:a,fillWidth:i,share:r,tokens:o,tokensTitle:l,dataAttrs:c}){return e.jsxs(I,{fillWidth:i,share:r,color:ie(s),ariaLabel:`${a} ${u(r)}`,dataAttrs:c,children:[e.jsxs("div",{className:"relative flex min-w-0 items-center gap-2.5",children:[e.jsx("span",{className:`w-5 shrink-0 text-xs tabular-nums ${t<=3?"font-semibold text-oai-gray-700 dark:text-oai-gray-200":"text-oai-gray-400"}`,children:t}),e.jsx("span",{className:"grid size-6 shrink-0 place-items-center text-oai-gray-700 dark:text-oai-gray-200",children:e.jsx(F,{provider:s,size:15})}),e.jsx("span",{className:"truncate text-xs font-medium text-oai-gray-700 dark:text-oai-gray-200",title:a,children:a})]}),e.jsxs("div",{className:"relative min-w-[4.5rem] text-right",children:[e.jsx("div",{className:"text-xs font-semibold text-oai-black tabular-nums dark:text-white",title:l,children:o}),e.jsx("div",{className:"text-xs text-oai-gray-500 tabular-nums dark:text-oai-gray-400",children:u(r)})]})]})}function le({rows:t,formatTokensTooltip:s}){return t.length?e.jsxs("div",{className:"mt-7",children:[e.jsx(b,{children:n("leaderboard.community.modal.token_mix")}),e.jsx("div",{className:"flex h-1.5 overflow-hidden bg-oai-gray-100 dark:bg-oai-gray-800",children:t.map(a=>e.jsx("div",{style:{width:`${Math.max(Number(a.share)||0,0)}%`,backgroundColor:_[a.key]||"var(--community-muted)"},title:`${n(`leaderboard.community.modal.token_mix.${a.key}`)} · ${u(a.share)}`},a.key))}),e.jsx("div",{className:"mt-3 flex flex-wrap gap-x-5 gap-y-1.5",children:t.map(a=>e.jsxs("span",{className:"inline-flex items-center gap-1.5 text-xs text-oai-gray-500 dark:text-oai-gray-400",title:s(a.tokens),children:[e.jsx("span",{className:"size-2",style:{backgroundColor:_[a.key]||"var(--community-muted)"}}),n(`leaderboard.community.modal.token_mix.${a.key}`),e.jsx("span",{className:"tabular-nums text-oai-gray-700 dark:text-oai-gray-200",children:u(a.share)})]},a.key))})]}):null}function f({label:t,value:s,title:a,delta:i,deltaLabel:r,className:o=""}){return e.jsxs("div",{className:`min-w-0 ${o}`,children:[e.jsx("div",{className:"truncate text-xs text-oai-gray-500 dark:text-oai-gray-400",children:t}),e.jsxs("div",{className:"mt-1 flex min-w-0 items-baseline gap-2",children:[e.jsx("span",{className:"truncate text-base font-semibold leading-none tracking-tight tabular-nums text-oai-black dark:text-white",title:a,children:s}),e.jsx(C,{value:i,label:r})]})]})}function de({trendRows:t,activeDevelopers30d:s,developerGrowthPct:a,formatTokens:i,formatTokensTooltip:r}){if(!t.length)return null;const o=t.reduce((m,h)=>m+h.value,0),l=t.reduce((m,h)=>m.value<h.value?h:m,t[0]),c=o/t.length;return e.jsxs("div",{className:"mt-5 grid grid-cols-3 divide-x divide-oai-gray-100 border-y border-oai-gray-100 py-3.5 dark:divide-oai-gray-800/80 dark:border-oai-gray-800/80",children:[e.jsx(f,{className:"pr-4",label:n("leaderboard.community.modal.daily_average"),value:i(c),title:r(c)}),e.jsx(f,{className:"px-4",label:`${n("leaderboard.community.modal.peak_day")} · ${l.day}`,value:i(l.value),title:r(l.value)}),e.jsx(f,{className:"pl-4",label:n("leaderboard.community.modal.active_30d"),value:p(s),title:Number(s).toLocaleString(),delta:a,deltaLabel:n("leaderboard.community.modal.vs_previous_week")})]})}function me({trendRows:t,mix:s,activeDevelopers30d:a,developerGrowthPct:i,formatTokens:r,formatTokensTooltip:o}){return!t.length&&!s.length?e.jsx(k,{}):e.jsxs("div",{children:[e.jsx(b,{detail:n("leaderboard.community.modal.utc_note"),children:n("leaderboard.community.modal.community_activity")}),e.jsx(G,{embedded:!0,rows:t,from:t[0]?.day,to:t.at(-1)?.day,chartHeightClass:"h-44"}),e.jsx(de,{trendRows:t,activeDevelopers30d:a,developerGrowthPct:i,formatTokens:r,formatTokensTooltip:o}),e.jsx(le,{rows:s,formatTokensTooltip:o})]})}function ce({rows:t,maxShare:s,formatTokens:a,formatTokensTooltip:i}){return t.length?e.jsx("div",{className:"space-y-0.5",children:t.map((r,o)=>e.jsx(A,{rank:o+1,dataAttrs:{"data-provider-rank":o+1,"data-provider-name":r.name},provider:String(r.name).toUpperCase(),name:r.name,fillWidth:Math.max((Number(r.share)||0)/s*100,2),share:r.share,tokens:a(r.tokens),tokensTitle:i(r.tokens)},r.name))}):e.jsx(k,{})}function xe({rows:t,maxShare:s,formatTokens:a,formatTokensTooltip:i}){return t.length?e.jsx("div",{className:"space-y-0.5",children:t.map((r,o)=>{const l=Number(r.share)||0;return e.jsx(A,{rank:o+1,dataAttrs:{"data-model-rank":o+1,"data-model-name":r.name},provider:oe(r.name),name:r.name,fillWidth:Math.max(l/s*100,2),share:l,tokens:a(r.tokens),tokensTitle:i(r.tokens)},r.name)})}):e.jsx(k,{})}function ue({name:t}){const s=String(t||"").toLowerCase();return s==="darwin"?e.jsx(K,{className:"size-3.5"}):s==="win32"?e.jsx(U,{className:"size-3.5"}):e.jsx(V,{className:"size-4","aria-hidden":"true"})}const he=.175,ye=.3;function ge({bands:t,formatTokensTooltip:s}){const a=t.reduce((r,o)=>Math.max(r,Number(o.developer_share)||0),0)||1,i=r=>`${n(`leaderboard.community.modal.band.${r.key}`)} · ${n("leaderboard.community.modal.developer_count",{count:p(r.developers)})} (${s(r.tokens)})`;return e.jsxs("div",{children:[e.jsx("div",{className:"flex gap-2 border-b border-oai-gray-200 dark:border-oai-gray-800",children:t.map((r,o)=>{const l=Number(r.developer_share)||0,c=Math.max(l/a*80,2);return e.jsxs("div",{className:"relative h-32 min-w-0 flex-1",title:i(r),children:[e.jsx("div",{role:"progressbar","aria-label":n("leaderboard.community.modal.developer_share_title",{share:u(l)}),"aria-valuemin":"0","aria-valuemax":"100","aria-valuenow":l,className:"absolute inset-x-0 bottom-0",style:{height:`${c}%`,backgroundColor:"var(--oai-blue)",opacity:ye+o*he}}),e.jsx("div",{className:"absolute inset-x-0 text-center text-xs font-medium tabular-nums text-oai-gray-700 dark:text-oai-gray-200",style:{bottom:`calc(${c}% + 4px)`},children:u(l)})]},r.key)})}),e.jsx("div",{className:"mt-2 flex gap-2",children:t.map(r=>e.jsxs("div",{className:"min-w-0 flex-1 text-center",title:i(r),children:[e.jsx("div",{className:"truncate text-xs font-medium text-oai-gray-700 dark:text-oai-gray-200",children:n(`leaderboard.community.modal.band.${r.key}`)}),e.jsx("div",{className:"truncate text-xs tabular-nums text-oai-gray-500 dark:text-oai-gray-400",children:p(r.developers)})]},r.key))})]})}function pe({bands:t,platforms:s,formatTokensTooltip:a}){return!t.length&&!s.length?e.jsx(k,{}):e.jsxs("div",{className:"space-y-8",children:[t.length?e.jsxs("div",{children:[e.jsx(b,{children:n("leaderboard.community.modal.usage_distribution")}),e.jsx(ge,{bands:t,formatTokensTooltip:a})]}):null,s.length?e.jsxs("div",{children:[e.jsx(b,{children:n("leaderboard.community.modal.platforms")}),e.jsx("div",{className:"space-y-0.5",children:s.map(i=>e.jsxs(I,{fillWidth:Math.max(Number(i.share)||0,1),share:i.share,ariaLabel:`${n(`leaderboard.community.modal.platform.${i.name}`)} ${u(i.share)}`,children:[e.jsxs("div",{className:"relative flex min-w-0 items-center gap-2.5",children:[e.jsx("span",{className:"grid size-6 shrink-0 place-items-center text-oai-gray-700 dark:text-oai-gray-200",children:e.jsx(ue,{name:i.name})}),e.jsx("span",{className:"truncate text-xs font-medium text-oai-gray-700 dark:text-oai-gray-200",children:n(`leaderboard.community.modal.platform.${i.name}`)})]}),e.jsxs("div",{className:"relative min-w-[4.5rem] text-right",children:[e.jsx("div",{className:"text-xs font-semibold text-oai-black tabular-nums dark:text-white",children:u(i.share)}),e.jsx("div",{className:"text-xs text-oai-gray-500 tabular-nums dark:text-oai-gray-400",title:Number(i.machines||0).toLocaleString(),children:n("leaderboard.community.modal.machine_count",{count:p(i.machines)})})]})]},i.name))})]}):null]})}function ve({isOpen:t,onClose:s,communityStats:a}){const{formatTokens:i,formatTokensTooltip:r}=B(),[o,l]=D.useState("overview"),c=a?.status==="ready",m=D.useMemo(()=>{if(!c)return null;const d=(Array.isArray(a.providers)?a.providers:[]).slice(0,O),x=(Array.isArray(a.topModels)?a.topModels:[]).slice(0,O),y=v=>v.reduce((E,$)=>Math.max(E,Number($.share)||0),0)||1;return{providers:d,models:x,providersMaxShare:y(d),modelsMaxShare:y(x),trendRows:(Array.isArray(a.dailyGrowth)?a.dailyGrowth:[]).map(v=>({day:ne(v.day),value:Number(v.tokens)||0})),mix:Array.isArray(a.tokenMix)?a.tokenMix:[],bands:Array.isArray(a.userDistribution)?a.userDistribution:[],platforms:Array.isArray(a.platforms)?a.platforms:[]}},[c,a]);if(!m)return null;const h=Number(a.tokenFloor)||0,N=Number(a.activeDevelopersTotal)||Number(a.totalEntries)||0,w=Number(a.tokens30d)||0,T=d=>{if(d.key!=="ArrowRight"&&d.key!=="ArrowLeft")return;d.preventDefault();const x=d.key==="ArrowRight"?1:-1,y=g[(g.indexOf(o)+x+g.length)%g.length];l(y),d.currentTarget.querySelector(`[data-tab-id="${y}"]`)?.focus()};return e.jsx(X,{open:t,onOpenChange:d=>!d&&s(),children:e.jsxs(Y,{children:[e.jsx(Z,{className:"fixed inset-0 z-[100] bg-black/50 backdrop-blur-[3px] transition-opacity duration-200 ease-out data-[ending-style]:opacity-0 data-[starting-style]:opacity-0"}),e.jsx(H,{className:"fixed inset-0 z-[101] flex items-center justify-center p-2 sm:p-5",children:e.jsxs(q,{className:"relative flex h-[min(42rem,92vh)] w-full max-w-xl flex-col overflow-hidden rounded-2xl bg-white shadow-2xl ring-1 ring-black/10 transition-[opacity,transform] duration-[220ms] ease-[cubic-bezier(0.16,1,0.3,1)] data-[ending-style]:translate-y-3 data-[ending-style]:scale-[0.98] data-[ending-style]:opacity-0 data-[starting-style]:translate-y-3 data-[starting-style]:scale-[0.98] data-[starting-style]:opacity-0 dark:bg-oai-gray-950 dark:ring-white/10",children:[e.jsxs("header",{"data-testid":"community-metrics-header",className:"shrink-0 px-5 pt-5 sm:px-6",children:[e.jsxs("div",{className:"flex items-start justify-between gap-4",children:[e.jsxs("div",{children:[e.jsx(J,{className:"text-lg font-semibold tracking-[-0.02em] text-oai-black dark:text-white",children:n("leaderboard.community.modal.title")}),e.jsx(Q,{className:"sr-only",children:n("leaderboard.community.modal.description")})]}),e.jsx(ee,{className:"-mr-1 -mt-1 grid size-8 place-items-center rounded-full text-oai-gray-400 transition-colors hover:bg-black/5 hover:text-oai-black dark:hover:bg-white/10 dark:hover:text-white","aria-label":n("shared.dialog.close"),children:e.jsx(z,{className:"size-4"})})]}),e.jsxs("div",{className:"mt-4 grid grid-cols-3 gap-x-5",children:[e.jsx(j,{label:n("leaderboard.community.modal.total_tokens"),value:i(h),title:r(h)}),e.jsx(j,{label:n("leaderboard.community.modal.tokens_30d"),value:i(w),title:r(w),delta:a.tokenGrowthPct,deltaLabel:n("leaderboard.community.modal.vs_previous_week")}),e.jsx(j,{label:n("leaderboard.community.modal.contributors"),value:p(N),title:N.toLocaleString()})]}),e.jsx("div",{className:"mt-4 flex items-end overflow-x-auto border-b border-oai-gray-100 dark:border-oai-gray-900",role:"tablist","aria-label":n("leaderboard.community.modal.views"),onKeyDown:T,children:g.map(d=>{const x=o===d;return e.jsxs("button",{type:"button",role:"tab","data-tab-id":d,"aria-selected":x,tabIndex:x?0:-1,onClick:()=>l(d),className:`relative mr-5 h-10 shrink-0 text-sm font-medium transition-colors last:mr-0 ${x?"text-oai-black dark:text-white":"text-oai-gray-500 hover:text-oai-gray-700 dark:text-oai-gray-400 dark:hover:text-oai-gray-200"}`,children:[n(`leaderboard.community.modal.tab.${d}`),x?e.jsx(M.span,{layoutId:"community-metrics-tab-underline",className:"absolute inset-x-0 -bottom-px h-0.5 rounded-full bg-oai-black dark:bg-white",transition:{type:"spring",stiffness:420,damping:34}}):null]},d)})})]}),e.jsx("div",{"data-testid":"community-metrics-content",className:"min-h-0 flex-1 overflow-y-auto px-5 py-5 sm:px-6",children:e.jsxs(M.div,{initial:{opacity:0,y:4},animate:{opacity:1,y:0},transition:{duration:.18,ease:[.16,1,.3,1]},className:"h-full",children:[o==="overview"?e.jsx(me,{trendRows:m.trendRows,mix:m.mix,activeDevelopers30d:Number(a.activeDevelopers30d)||0,developerGrowthPct:a.developerGrowthPct,formatTokens:i,formatTokensTooltip:r}):null,o==="providers"?e.jsx(ce,{rows:m.providers,maxShare:m.providersMaxShare,formatTokens:i,formatTokensTooltip:r}):null,o==="community"?e.jsx(pe,{bands:m.bands,platforms:m.platforms,formatTokensTooltip:r}):null,o==="models"?e.jsx(xe,{rows:m.models,maxShare:m.modelsMaxShare,formatTokens:i,formatTokensTooltip:r}):null]},o)})]})})]})})}function Pe(t){return e.jsx(R,{mode:P.COMPACT,children:e.jsx(ve,{...t})})}export{Pe as CommunityStatsModal};