tokentracker-cli 0.96.1 → 0.96.3

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 (145) hide show
  1. package/README.de.md +34 -25
  2. package/README.ja.md +38 -27
  3. package/README.ko.md +39 -28
  4. package/README.md +36 -28
  5. package/README.zh-CN.md +36 -25
  6. package/dashboard/dist/assets/{AchievementBadge-DKyElm0T.js → AchievementBadge-BX4iBBYM.js} +1 -1
  7. package/dashboard/dist/assets/{AchievementsPage-BvCx3wND.js → AchievementsPage-DRYRuCbh.js} +2 -2
  8. package/dashboard/dist/assets/{AchievementsSection-DN69LzXX.js → AchievementsSection-BM_JWWhJ.js} +1 -1
  9. package/dashboard/dist/assets/{BadgeDetailModal-DGiKdZfz.js → BadgeDetailModal-DxoxXst2.js} +1 -1
  10. package/dashboard/dist/assets/{BotAnimated-zN8rH7_K.js → BotAnimated-BVpFxG8u.js} +1 -1
  11. package/dashboard/dist/assets/{Card-COWo-ClV.js → Card-BJJs701t.js} +1 -1
  12. package/dashboard/dist/assets/{ClawdAnimated-BWsB7Eat.js → ClawdAnimated-46wPZ_Tc.js} +2 -2
  13. package/dashboard/dist/assets/{CommandPalette-BfvIwuoJ.js → CommandPalette-B_zkc7IU.js} +1 -1
  14. package/dashboard/dist/assets/{CommunityStatsModal-BQWg7R_y.js → CommunityStatsModal-M2k5rIf1.js} +1 -1
  15. package/dashboard/dist/assets/{ConfirmModal-C7ABSI-O.js → ConfirmModal-DBvOTcAQ.js} +1 -1
  16. package/dashboard/dist/assets/{Controls-vMzEZzej.js → Controls-BCkjPMSH.js} +1 -1
  17. package/dashboard/dist/assets/DashboardPage-3bHhou09.js +1 -0
  18. package/dashboard/dist/assets/{DevicePage-3ejUBLWM.js → DevicePage-DFBUce8C.js} +1 -1
  19. package/dashboard/dist/assets/DialogClose-kvDd8h3V.js +1 -0
  20. package/dashboard/dist/assets/DialogDescription-BMyOicfz.js +1 -0
  21. package/dashboard/dist/assets/DialogTitle-DL5nvD31.js +1 -0
  22. package/dashboard/dist/assets/{FadeIn-D7Jcfz1N.js → FadeIn-B-Bg6SEy.js} +1 -1
  23. package/dashboard/dist/assets/{HeaderGithubStar-D--Rx5BS.js → HeaderGithubStar-BExz0hh4.js} +1 -1
  24. package/dashboard/dist/assets/{HoverTooltip-n1W0z3Wr.js → HoverTooltip-Dj5zemYT.js} +1 -1
  25. package/dashboard/dist/assets/{IpCheckPage-D202tl9B.js → IpCheckPage-DN7FuJ_v.js} +1 -1
  26. package/dashboard/dist/assets/{LandingPage-D6yL2EZo.js → LandingPage-9dKgSBRy.js} +2 -2
  27. package/dashboard/dist/assets/{LeaderboardAvatar-ChFg2mtA.js → LeaderboardAvatar-00V95XIg.js} +1 -1
  28. package/dashboard/dist/assets/{LeaderboardPage-BXHwGs8r.js → LeaderboardPage-DkKfUt0Z.js} +2 -2
  29. package/dashboard/dist/assets/{LeaderboardProfileModal-B5LoOpCk.js → LeaderboardProfileModal-C9sIHBgN.js} +4 -4
  30. package/dashboard/dist/assets/{LeaderboardProfilePage-jsukhRz8.js → LeaderboardProfilePage-BAiLMsEb.js} +1 -1
  31. package/dashboard/dist/assets/LimitsPage-BuenTaHa.js +8 -0
  32. package/dashboard/dist/assets/{LocalOnlyNotice-Du0lz657.js → LocalOnlyNotice-CKhY27Lr.js} +1 -1
  33. package/dashboard/dist/assets/{LoginCard-BFEX9x7-.js → LoginCard-BbvoBx5N.js} +1 -1
  34. package/dashboard/dist/assets/{LoginModal-DqC7_d35.js → LoginModal-P7zIkBhO.js} +1 -1
  35. package/dashboard/dist/assets/{LoginPage-Dzx8N7HE.js → LoginPage-Bo0bR26x.js} +1 -1
  36. package/dashboard/dist/assets/PetPage-C6i9bSSl.js +1 -0
  37. package/dashboard/dist/assets/PopoverPopup-h0gf36vg.js +1 -0
  38. package/dashboard/dist/assets/ProviderIcon-Dm8fwPdj.js +1 -0
  39. package/dashboard/dist/assets/{ResetPasswordPage-x_GR79sR.js → ResetPasswordPage-DvSJ8kg0.js} +1 -1
  40. package/dashboard/dist/assets/{SegmentedControl-BxDiDV4R.js → SegmentedControl-CP7oiGZE.js} +1 -1
  41. package/dashboard/dist/assets/Select-iwP92x9C.js +1 -0
  42. package/dashboard/dist/assets/SelectItemText-DzljA15_.js +1 -0
  43. package/dashboard/dist/assets/{ServiceStatusPage-BVQuVdjg.js → ServiceStatusPage-uD4D0Tid.js} +1 -1
  44. package/dashboard/dist/assets/{SessionsPage-DE39wNIO.js → SessionsPage-Cmb1ssNP.js} +2 -2
  45. package/dashboard/dist/assets/SettingsPage-D8ryg1Pv.js +2 -0
  46. package/dashboard/dist/assets/SkillsPage-eXI09q03.js +1 -0
  47. package/dashboard/dist/assets/{TokenGalaxy-BPM779Wb.js → TokenGalaxy-f2djrlUv.js} +1 -1
  48. package/dashboard/dist/assets/ToolbarRootContext-CDxCPYLv.js +1 -0
  49. package/dashboard/dist/assets/{TrendMonitor-BkNy-o1D.js → TrendMonitor-C419X22G.js} +1 -1
  50. package/dashboard/dist/assets/{WidgetsPage-Bpg6H-ZB.js → WidgetsPage-AYq__pOO.js} +1 -1
  51. package/dashboard/dist/assets/{WrappedPage-C6-NM-RO.js → WrappedPage-Bsm9k5XI.js} +1 -1
  52. package/dashboard/dist/assets/{__vite-browser-external-CvMMIjhi.css → __vite-browser-external-5P7me0Qq.css} +1 -1
  53. package/dashboard/dist/assets/__vite-browser-external-BDkeh90N.js +45 -0
  54. package/dashboard/dist/assets/{agent-logos-DpbkRqXT.js → agent-logos-B6Vg-4_1.js} +1 -1
  55. package/dashboard/dist/assets/{arrow-up-right-BjOtWUVs.js → arrow-up-right-DIQXwaOD.js} +1 -1
  56. package/dashboard/dist/assets/{check-BlxnX5jZ.js → check-CgD_bV39.js} +1 -1
  57. package/dashboard/dist/assets/composite-BIgEqWZD.js +1 -0
  58. package/dashboard/dist/assets/{copy-CIsWjLY5.js → copy-DlM1BHdp.js} +1 -1
  59. package/dashboard/dist/assets/{download-QDaQbQdd.js → download-CQFG-VIY.js} +1 -1
  60. package/dashboard/dist/assets/{external-link-DPV4VU3m.js → external-link-BQyDqGxz.js} +1 -1
  61. package/dashboard/dist/assets/{flame-B_X-7VNp.js → flame-BamzyXzF.js} +1 -1
  62. package/dashboard/dist/assets/{format-tokens-DEx2TJj8.js → format-tokens-C4G-_xYt.js} +1 -1
  63. package/dashboard/dist/assets/{icons-983RyGSl.js → icons-Ntscvl1a.js} +1 -1
  64. package/dashboard/dist/assets/{index-qtonw_x4.js → index-CUrcPqgp.js} +1 -1
  65. package/dashboard/dist/assets/{index-BONYA8HZ.js → index-DGxgsNoQ.js} +1 -1
  66. package/dashboard/dist/assets/{index-Cjs5stC0.js → index-FRIt5mlv.js} +1 -1
  67. package/dashboard/dist/assets/{info-DpVOhXZI.js → info-CXytWcZi.js} +1 -1
  68. package/dashboard/dist/assets/{link-2-CBro-upz.js → link-2-Dexoo_jO.js} +1 -1
  69. package/dashboard/dist/assets/{loader-circle-wYFdSyOd.js → loader-circle-Cn0JHc8t.js} +1 -1
  70. package/dashboard/dist/assets/{maximize-2-DhTyQ4ly.js → maximize-2-DLh7gYuP.js} +1 -1
  71. package/dashboard/dist/assets/{modifiers.esm-CPljwVhN.js → modifiers.esm-DOh_xtyt.js} +1 -1
  72. package/dashboard/dist/assets/module-DUSXqJtA.js +3 -0
  73. package/dashboard/dist/assets/{monitor-smartphone-D_wr-HOR.js → monitor-smartphone-CpqS9Ior.js} +1 -1
  74. package/dashboard/dist/assets/{plus-BNjfkBEF.js → plus-bfQV7nyv.js} +1 -1
  75. package/dashboard/dist/assets/provider-display-Bf9pW06U.js +42 -0
  76. package/dashboard/dist/assets/{react-yHv9MTUA.js → react-BmFZKA_b.js} +1 -1
  77. package/dashboard/dist/assets/{search-Dfak92ZA.js → search-l3jJ2fzC.js} +1 -1
  78. package/dashboard/dist/assets/{skills-api-By2PdNa1.js → skills-api-CWmLnaxn.js} +1 -1
  79. package/dashboard/dist/assets/{sortable.esm-B9xy66_v.js → sortable.esm-DLgyXLHM.js} +1 -1
  80. package/dashboard/dist/assets/store-JM-a8ElZ.js +1 -0
  81. package/dashboard/dist/assets/{terminal-pbXWBmZM.js → terminal-6aY2Sn8P.js} +1 -1
  82. package/dashboard/dist/assets/{trash-2-CboDPwbW.js → trash-D5oon5pT.js} +1 -1
  83. package/dashboard/dist/assets/{use-community-stats-D6HTdnHq.js → use-community-stats-ZWUMFwzc.js} +1 -1
  84. package/dashboard/dist/assets/{use-latest-request-guard-hwSr9Cam.js → use-latest-request-guard-BDaXr4gK.js} +1 -1
  85. package/dashboard/dist/assets/use-limits-display-prefs-yXXToNIc.js +1 -0
  86. package/dashboard/dist/assets/{use-ordered-list-OtfOqrNb.js → use-ordered-list-C6RG8N6c.js} +1 -1
  87. package/dashboard/dist/assets/use-reduced-motion-BqDKim_3.js +1 -0
  88. package/dashboard/dist/assets/{use-session-efficiency-pref-BScvtS8F.js → use-session-efficiency-pref-DpgZO_Lx.js} +1 -1
  89. package/dashboard/dist/assets/{use-transform-Cy1ykjEs.js → use-transform-D42P7jkd.js} +1 -1
  90. package/dashboard/dist/assets/use-usage-limits-CV6puFwq.js +1 -0
  91. package/dashboard/dist/assets/{useCurrency-VqF3cy1C.js → useCurrency-GWBmOrCR.js} +1 -1
  92. package/dashboard/dist/assets/{useTokenFormat-Cgub4MXu.js → useTokenFormat-Beybkb0v.js} +1 -1
  93. package/dashboard/dist/assets/{zap-DStzvQJI.js → zap-CEq2616W.js} +1 -1
  94. package/dashboard/dist/brand-logos/acode-dark.png +0 -0
  95. package/dashboard/dist/brand-logos/acode.png +0 -0
  96. package/dashboard/dist/brand-logos/commandcode.svg +1 -0
  97. package/dashboard/dist/brand-logos/devin.svg +6 -0
  98. package/dashboard/dist/index.html +20 -16
  99. package/dashboard/dist/ip-check.html +2 -2
  100. package/dashboard/dist/leaderboard.html +2 -2
  101. package/dashboard/dist/llms.txt +4 -2
  102. package/dashboard/dist/share.html +2 -2
  103. package/package.json +2 -2
  104. package/src/commands/init.js +114 -4
  105. package/src/commands/status.js +37 -0
  106. package/src/commands/sync.js +92 -10
  107. package/src/commands/uninstall.js +23 -0
  108. package/src/lib/codex-config.js +42 -0
  109. package/src/lib/codex-rollout-parser.js +2 -2
  110. package/src/lib/commandcode-limits.js +317 -0
  111. package/src/lib/devin-limits.js +249 -0
  112. package/src/lib/devin-usage.js +212 -0
  113. package/src/lib/local-api.js +14 -0
  114. package/src/lib/passive-mode.js +13 -1
  115. package/src/lib/pricing/curated-overrides.json +78 -1
  116. package/src/lib/pricing/index.js +11 -9
  117. package/src/lib/pricing/matcher.js +28 -0
  118. package/src/lib/pricing/seed-snapshot.json +1 -1
  119. package/src/lib/rollout.js +1473 -147
  120. package/src/lib/session-analytics.js +2 -2
  121. package/src/lib/skills-manager.js +5 -0
  122. package/src/lib/subscriptions.js +70 -16
  123. package/src/lib/usage-limits.js +240 -49
  124. package/dashboard/dist/assets/DashboardPage-QBhbCMZF.js +0 -1
  125. package/dashboard/dist/assets/DialogClose-D_dvtfGV.js +0 -1
  126. package/dashboard/dist/assets/DialogDescription-ryGYdQjX.js +0 -1
  127. package/dashboard/dist/assets/DialogTitle-_ahf0z8b.js +0 -1
  128. package/dashboard/dist/assets/LimitsPage-PvqkOOwp.js +0 -6
  129. package/dashboard/dist/assets/PetPage-CNlWHF4z.js +0 -1
  130. package/dashboard/dist/assets/PopoverPopup-D9jadD3j.js +0 -1
  131. package/dashboard/dist/assets/ProviderIcon-Caz5PuAm.js +0 -1
  132. package/dashboard/dist/assets/Select-B5o-1sEl.js +0 -1
  133. package/dashboard/dist/assets/SelectItemText-CiA6L7Wk.js +0 -1
  134. package/dashboard/dist/assets/SettingsPage-DJrb_fsK.js +0 -2
  135. package/dashboard/dist/assets/SkillsPage-COk-YQQ5.js +0 -1
  136. package/dashboard/dist/assets/ToolbarRootContext-CozJT6Vs.js +0 -1
  137. package/dashboard/dist/assets/__vite-browser-external-BGnq8Ebm.js +0 -45
  138. package/dashboard/dist/assets/composite-Dxmivlk4.js +0 -1
  139. package/dashboard/dist/assets/limits-providers-BUoquSA-.js +0 -1
  140. package/dashboard/dist/assets/module-xpSFyNBK.js +0 -3
  141. package/dashboard/dist/assets/provider-display-BOBtUyFi.js +0 -42
  142. package/dashboard/dist/assets/store-oQZR4d9M.js +0 -1
  143. package/dashboard/dist/assets/use-limits-display-prefs-B-pbwUS8.js +0 -1
  144. package/dashboard/dist/assets/use-reduced-motion-DGXcZBX3.js +0 -1
  145. package/dashboard/dist/assets/use-usage-limits-CSMb2zYW.js +0 -1
package/README.md CHANGED
@@ -6,7 +6,7 @@
6
6
 
7
7
  ### Track every AI token — then bring your usage to life
8
8
 
9
- An accurate, local-first token usage and cost dashboard for **36 AI coding tools** — plus a desktop pet, **4 native widgets**, and **15 achievement tracks**. No cloud account, no API keys, no setup.
9
+ An accurate, local-first token usage and cost dashboard for **38 AI coding tools** — plus a desktop pet, **4 native widgets**, and **15 achievement tracks**. No cloud account, no API keys, no setup.
10
10
 
11
11
  [![npm version](https://img.shields.io/npm/v/tokentracker-cli.svg?color=blue)](https://www.npmjs.com/package/tokentracker-cli)
12
12
  [![npm downloads](https://img.shields.io/npm/dm/tokentracker-cli.svg?color=brightgreen)](https://www.npmjs.com/package/tokentracker-cli)
@@ -85,7 +85,7 @@ That's it. First run installs hooks, syncs your data, and opens the dashboard at
85
85
  **What you get in 30 seconds:**
86
86
  - 📊 A local dashboard at `localhost:7680` with usage trends, model breakdown, cost analysis
87
87
  - 🔌 Auto-detected hooks for every supported AI tool you have installed
88
- - 🏠 100% local — no account, no API keys, no network calls (except optional leaderboard)
88
+ - 🏠 Local-first — parses logs on your machine; no account or API keys required
89
89
  - 🧩 *Optional:* a Skills tab that browses 250+ public skills and syncs them across Claude · Codex · Grok · Antigravity · Gemini · OpenCode · Hermes
90
90
 
91
91
  > **Want a native desktop app?**
@@ -147,21 +147,21 @@ An Arch `PKGBUILD` for a local pacman install lives in `TokenTrackerLinux/packag
147
147
 
148
148
  ## ✨ Features
149
149
 
150
- - 🔌 **36 AI tools out of the box** — Claude Code, Codex CLI, Cursor, Gemini CLI, Antigravity, Kiro, OpenCode, OpenClaw, Every Code, Hermes Agent, GitHub Copilot, Kimi Code, CodeBuddy, WorkBuddy, Grok Build, oh-my-pi, pi, Dots, Prime Agent, Craft Agents, Reasonix, Kilo CLI, Kilo Code, Roo Code, Zed Agent, Goose, Droid, Mimo Code, ZCode, Qoder, AnythingLLM Desktop, Claude Science, DeepSeek Harness, TRAE Work CN, LM Studio, Unsloth Studio
151
- - 🏠 **100% local** — Token data never leaves your machine. No account, no API keys.
150
+ - 🔌 **38 AI tools out of the box** — Claude Code, Codex CLI, AStudio, Cursor, Gemini CLI, Antigravity, Kiro, OpenCode, OpenClaw, Every Code, Hermes Agent, GitHub Copilot, Kimi Code, CodeBuddy, WorkBuddy, Grok Build, oh-my-pi, pi, Dots, Prime Agent, Craft Agents, Reasonix, Kilo CLI, Kilo Code, Roo Code, Zed Agent, Goose, Droid, Mimo Code, ZCode, Qoder, AnythingLLM Desktop, Claude Science, DeepSeek Harness, TRAE Work CN, LM Studio, Unsloth Studio, Devin CLI
151
+ - 🏠 **Local-first** — Runs on your machine. Parses logs locally with no account or API keys needed.
152
152
  - 🚀 **Zero config** — Hooks auto-install on first run. From zero to dashboard in 30 seconds.
153
153
  - 📊 **Beautiful dashboard** — Usage trends, cost breakdowns by model, GitHub-style activity heatmap, project attribution
154
154
  - 🖥️ **Native desktop app** — macOS menu bar (+ widgets) and Windows system tray, each with an embedded server and the dashboard in a native webview
155
155
  - 🐾 **Desktop pet** — A pixel companion powered by real coding activity: it works when you work, celebrates streaks, and sleeps when you rest
156
156
  - 🎨 **4 desktop widgets** — Pin Usage / Activity Heatmap / Top Models / Usage Limits to your desktop
157
157
  - 🏆 **15 achievement tracks** — Turn daily usage, streaks, tools, models, and milestones into collectible badges worth sharing
158
- - 📈 **Real-time usage limits** — Claude / Codex / Cursor / Gemini / Kimi / Kiro / Grok / Copilot / Antigravity / ZCode / OpenCode Go / Qoder / Qoder CN quota windows, with last-good caching when a local provider app is temporarily closed
158
+ - 📈 **Real-time usage limits** — Claude / Codex / Cursor / Gemini / Kimi / Kiro / Grok / Copilot / Antigravity / ZCode / OpenCode Go / Qoder / Qoder CN / Command Code / Ark Coding Plan / Ark Agent Plan / Devin quota windows, with last-good caching when a local provider app is temporarily closed
159
159
  - 🟢 **Service Status page** — live operational and incident status from 8 official provider status pages
160
160
  - 💰 **Cost engine** — 2,200+ models priced via [LiteLLM](https://github.com/BerriAI/litellm/blob/main/model_prices_and_context_window.json) (auto-refreshed daily) + curated overrides for niche tools (Kiro, Cursor Composer, Kimi, CodeBuddy hy3); 24h disk cache + bundled offline snapshot mean accurate USD without an internet connection. Models without published vendor pricing (e.g. Tencent hy3-preview) are tracked by tokens but show $0 cost until the vendor publishes a rate.
161
161
  - 🌐 **Optional leaderboard** — Compare with developers worldwide; drag-to-reorder columns to focus on the providers you care about (opt-in, sign in to participate)
162
- - 🔄 **Cross-device account view** — Opt in to cloud sync and the dashboard merges your usage across every machine you work on (laptop + desktop + server) into one combined view — totals, trends, heatmap and model breakdown all device-aggregated (opt-in, sign in; the default local-only experience stays instant and offline)
162
+ - 🔄 **Cross-device account view** — Optional cloud sync merges usage across your machines (laptop, desktop, server) into one view. Off by default; local tracking stays independent.
163
163
  - 🧩 **Optional Skills tab** — browse 250+ public skills from `anthropics/skills`, `ComposioHQ/awesome-claude-skills`, `skills.sh` and any GitHub repo you add; sync them across Claude / Codex / Grok / Antigravity / Gemini / OpenCode / Hermes with named targets and one-click Undo
164
- - 🔒 **Privacy-first** — Only token counts and timestamps. Never prompts, responses, or file contents.
164
+ - 🔒 **Privacy-first** — Local usage metrics only (token counts, timestamps, and model names). Prompts, completions, and code never leave your machine.
165
165
 
166
166
  ---
167
167
 
@@ -203,7 +203,7 @@ An Arch `PKGBUILD` for a local pacman install lives in `TokenTrackerLinux/packag
203
203
  <tr>
204
204
  <td colspan="2">
205
205
 
206
- **Skills Manager** — browse 250+ public skills from GitHub & `skills.sh`, install once, sync to Claude / Codex / Grok / Antigravity / Gemini / OpenCode / Hermes. Per-target toggles, one-click Undo, no manual file copying.
206
+ **Skills Manager** — browse 250+ public skills from GitHub & `skills.sh`, install once, sync to Claude / Codex / AStudio / Grok / Antigravity / Gemini / OpenCode / Hermes. Per-target toggles, one-click Undo, no manual file copying.
207
207
 
208
208
  <img src="https://raw.githubusercontent.com/xiufengsun/tokentracker/main/docs/screenshots/skills.png" alt="Skills Manager" />
209
209
 
@@ -237,6 +237,7 @@ An Arch `PKGBUILD` for a local pacman install lives in `TokenTrackerLinux/packag
237
237
  |---|---|---|
238
238
  | **Claude Code** | ✅ Auto | SessionEnd hook in `settings.json` |
239
239
  | **Codex CLI** | ✅ Auto | TOML notify hook in `config.toml` |
240
+ | **AStudio** | ✅ Auto | TOML notify hook in `config.toml` |
240
241
  | **Cursor** | ✅ Auto | API + SQLite auth token |
241
242
  | **Kiro** | ✅ Auto | SQLite + JSONL hybrid |
242
243
  | **Gemini CLI** | ✅ Auto | SessionEnd hook |
@@ -269,15 +270,16 @@ An Arch `PKGBUILD` for a local pacman install lives in `TokenTrackerLinux/packag
269
270
  | **LM Studio** | ✅ Auto | Passive recursive reader for `~/.lmstudio/server-logs/**/*.log`; reads only final-response IDs, models, timestamps, and scalar `usage` counters from Chat Completions and Responses API records. Mirrored response IDs are deduplicated; prompt and response bodies are never retained. These developer-server records cover local inference and LM Link at zero marginal API cost, not Bionic Secure Cloud billing. |
270
271
  | **Unsloth Studio** | ✅ Auto | Passive SQLite reader for `$UNSLOTH_STUDIO_HOME/studio.db` (default `~/.unsloth/studio/studio.db`). Reads scalar `contextUsage` metadata and content-free `api_usage_events` only; prompts, replies, attachments, API subjects, credentials, and training metrics are excluded. Local routes stay zero-cost, while known paid provider routes use the model that actually answered for estimated token cost. |
271
272
  | **AnythingLLM Desktop** | ✅ Auto | Passive SQLite reader (`anythingllm-desktop/storage/anythingllm.db`; reads per-message token metrics only, never prompts or responses) |
273
+ | **Devin CLI** (Cognition) | ✅ Auto | Passive SQLite reader (`$XDG_DATA_HOME/devin/cli/sessions.db`, default `~/.local/share/devin/cli/sessions.db`; reads per-request usage metrics deduplicated by `request_id` — replay/fork/compaction copies never double-count — using the recorded generation model, never prompts, responses, or `cogs_json`). Devin models (`swe-2`, `swe-2-high`, `compactor`) currently have no pricing data, so their token counts are reported but excluded from dollar estimates — a $0 figure does not mean free usage. No native Windows data dir is known; a WSL install is read via `\\wsl$`. |
272
274
  | **Claude Science** | ✅ Auto | Passive SQLite reader (`~/.claude-science/operon-cli.db`; reads the `frames` table's token counters only, never prompts, artifacts or research content). No native Windows build — on Windows the app runs inside WSL and is read from there. |
273
275
  | **DeepSeek Harness** | ✅ Auto | Passive session reader (`~/.dsh/sessions/**/session.jsonl[.zstd]`; parses session header and assistant events, handles multi-frame zstd decompression) |
274
276
  | **TRAE Work CN** | ✅ Auto | **Requires an explicit opt-in: set `TOKENTRACKER_TRAE_CN_USAGE=1`.** Reading usage transmits the locally stored sign-in authorization to TRAE's internal API, so nothing is sent until you turn it on. Once opted in: during eligible non-background sync when local TRAE Work CN auth exists, reads session-token usage from the locally signed-in macOS / Windows app; its internal API may change |
275
277
 
276
278
  > **Do I need to install any plugin or hook manually?** No. `tokentracker` (or `tokentracker init`) handles everything on first run:
277
- > - **Hook-based** tools (Claude Code, Codex, Gemini, Every Code, **CodeBuddy**, **WorkBuddy**, **Grok Build**) — we write a SessionEnd hook or TOML notify entry into the tool's own config.
279
+ > - **Hook-based** tools (Claude Code, Codex, AStudio, Gemini, Every Code, **CodeBuddy**, **WorkBuddy**, **Grok Build**) — we write a SessionEnd hook or TOML notify entry into the tool's own config.
278
280
  > - **Plugin-based** tools (OpenCode, **OpenClaw**) — plugins ship inside the npm package. OpenClaw's session plugin lives at `~/.tokentracker/tracker/openclaw-plugin/openclaw-session-sync/`; we link and enable it via OpenClaw's own CLI, then set `hooks.allowConversationAccess=true` so OpenClaw permits the session-finished event that triggers sync. No download, no drag-and-drop.
279
281
  > - **oh-my-pi** — passive session scan is always the billing/token source of truth (`~/.omp/agent/sessions/**/*.jsonl`). When OMP is detected, `tokentracker init` also writes a managed notify extension to `~/.omp/agent/extensions/tokentracker-notify.ts` so turns can trigger near-real-time sync. That file is owned only when it carries TokenTracker's managed marker: a same-named user-authored extension is never overwritten or deleted. `tokentracker uninstall` removes the extension only while it is still managed.
280
- > - **Passive readers** (Cursor, Kiro, Hermes, Kimi Code, Copilot, **Grok Build**, **pi**, **Craft Agents**, **Reasonix**, **Kilo CLI**, **Kilo Code**, **Roo Code**, **Antigravity**, **Zed Agent**, **Goose**, **Droid**, **Mimo Code**, **ZCode**, **Qoder**, **LM Studio**, **Unsloth Studio**, **AnythingLLM Desktop**, **Claude Science**, **DeepSeek Harness**) — nothing is installed into those tools. We only read files they already produce (SQLite DB, JSONL, OTEL export, session logs). Copilot App / CLI usage is read per request from `~/.copilot/session-store.db`; `data.db` provides the one-time legacy adoption baseline and stays observe-only after the store becomes canonical, while the Chat extension and legacy CLI continue using OTEL. TokenTracker coordinates the sources so overlapping requests are counted once. Mixed App/CLI usage that predates adoption is retained as a `github-copilot-legacy` aggregate rather than assigned to a guessed request model.
282
+ > - **Passive readers** (Cursor, Kiro, Hermes, Kimi Code, Copilot, **Grok Build**, **pi**, **Craft Agents**, **Reasonix**, **Kilo CLI**, **Kilo Code**, **Roo Code**, **Antigravity**, **Zed Agent**, **Goose**, **Droid**, **Mimo Code**, **ZCode**, **Qoder**, **LM Studio**, **Unsloth Studio**, **AnythingLLM Desktop**, **Devin CLI**, **Claude Science**, **DeepSeek Harness**) — nothing is installed into those tools. We only read files they already produce (SQLite DB, JSONL, OTEL export, session logs). Copilot App / CLI usage is read per request from `~/.copilot/session-store.db`; `data.db` provides the one-time legacy adoption baseline and stays observe-only after the store becomes canonical, while the Chat extension and legacy CLI continue using OTEL. TokenTracker coordinates the sources so overlapping requests are counted once. Mixed App/CLI usage that predates adoption is retained as a `github-copilot-legacy` aggregate rather than assigned to a guessed request model.
281
283
  > - **Grok Build estimate** — current local telemetry exposes cumulative `updates.jsonl` `totalTokens`, but not a stable prompt/output/cache split; `signals.json` remains a fallback with `contextTokensUsed` snapshots. TokenTracker estimates Grok cost until per-call usage details are available.
282
284
  >
283
285
  > Run `tokentracker status` anytime to verify every integration's state. If something shows `skipped`, the `detail` column explains why (e.g. tool CLI not on `PATH`, config unreadable).
@@ -290,18 +292,19 @@ Missing your tool? [Open an issue](https://github.com/xiufengsun/TokenTracker/is
290
292
 
291
293
  ## 🆚 Why TokenTracker? <a id="ccusage-alternative"></a>
292
294
 
293
- > **Looking for a ccusage alternative with a GUI?** TokenTracker covers 36 tools (not just Claude Code), adds native macOS and Windows apps + desktop widgets, and de-duplicates token records correctly across providers — so your numbers match the providers' own billing.
295
+ > **Looking for a desktop app with widgets and a visual dashboard?** TokenTracker pairs local log parsing across AI coding tools with native desktop apps, desktop widgets, provider quota tracking, and optional cloud sync.
294
296
 
295
- | | **TokenTracker** | ccusage | Cursor stats |
296
- |--------------------------|:---:|:---:|:---:|
297
- | **AI tools supported** | **36** | 1 (Claude) | 1 (Cursor) |
298
- | **Local-first, no account** | ✅ | ✅ | ❌ |
299
- | **Native desktop app** | ✅ macOS + Windows | ❌ | ❌ |
300
- | **Desktop widgets** | ✅ 4 widgets | ❌ | ❌ |
301
- | **Rate-limit tracking** | ✅ 13 providers | ❌ | Cursor only |
302
- | **Accurate multi-provider dedup** | ✅ | ❌ ¹ | — |
303
-
304
- <sub>¹ `reqId`-based deduplication over-counts providers that omit a request ID (DeepSeek / Kimi / MiniMax / Claude sub-agents) by 1.6–3.7×. TokenTracker dedups on a composite key, so totals match each provider's own billing dashboard.</sub>
297
+ | Capability | **[TokenTracker](https://github.com/xiufengsun/TokenTracker)** | **[ccusage](https://github.com/ccusage/ccusage)** | **[Tokscale](https://github.com/junhoyeo/tokscale)** |
298
+ |---|:---:|:---:|:---:|
299
+ | **AI tools supported** | **38** | Multi-agent | Multi-agent |
300
+ | **Primary interface** | Desktop apps & web dashboard | Terminal CLI | Terminal TUI & CLI |
301
+ | **Local analysis** | ✅ | ✅ | ✅ |
302
+ | **Native desktop apps** | ✅ macOS, Windows, Linux | ❌ | ❌ |
303
+ | **Desktop widgets** | ✅ 4 widgets | ❌ | ❌ |
304
+ | **Rate-limit tracking** | ✅ 17 providers | Limited (Claude blocks) | Multiple providers |
305
+ | **Terminal analytics** | Basic CLI (`status`, `--json`) | Strong CLI reporting | Interactive TUI & CLI |
306
+ | **JSON export** | ✅ | ✅ | ✅ |
307
+ | **Public leaderboard** | Optional (opt-in) | ❌ | Optional (`submit`) |
305
308
 
306
309
  ---
307
310
 
@@ -309,7 +312,7 @@ Missing your tool? [Open an issue](https://github.com/xiufengsun/TokenTracker/is
309
312
 
310
313
  ```mermaid
311
314
  flowchart LR
312
- A["AI coding tools<br/>Claude Code · Codex · Cursor · Gemini · Kiro<br/>OpenCode · OpenClaw · Every Code · Hermes · Copilot<br/>Kimi Code · CodeBuddy · WorkBuddy · Grok Build · Kilo CLI · Kilo Code<br/>Antigravity · oh-my-pi · pi · Dots · Craft · Roo · Zed · Goose · Droid · Mimo · ZCode · Qoder · AnythingLLM · Claude Science · DeepSeek Harness · TRAE Work CN · LM Studio · Unsloth Studio"]
315
+ A["AI coding tools<br/>Claude Code · Codex · AStudio · Cursor · Gemini · Kiro<br/>OpenCode · OpenClaw · Every Code · Hermes · Copilot<br/>Kimi Code · CodeBuddy · WorkBuddy · Grok Build · Kilo CLI · Kilo Code<br/>Antigravity · oh-my-pi · pi · Dots · Craft · Roo · Zed · Goose · Droid · Mimo · ZCode · Qoder · AnythingLLM · Claude Science · DeepSeek Harness · TRAE Work CN · LM Studio · Unsloth Studio · Devin CLI"]
313
316
  A -->|hooks trigger| B[Token Tracker]
314
317
  B -->|parse logs<br/>30-min UTC buckets| C[(Local SQLite)]
315
318
  C --> D[Web Dashboard]
@@ -320,7 +323,7 @@ flowchart LR
320
323
 
321
324
  1. AI CLI tools generate logs during normal use
322
325
  2. Lightweight hooks detect changes and trigger sync (Cursor uses API instead of hooks)
323
- 3. Token counts parsed locally — never any prompt or response content
326
+ 3. Token counts parsed locally — prompts, responses, and code are never saved or uploaded
324
327
  4. Aggregated into 30-minute UTC buckets
325
328
  5. Dashboard, menu bar app, and widgets all read from the same local snapshot
326
329
 
@@ -330,12 +333,14 @@ flowchart LR
330
333
 
331
334
  > 📄 **[Full Privacy Policy](docs/PRIVACY.md)** — every network request the app can make, what each one sends, and how to switch it off.
332
335
 
336
+ TokenTracker processes your usage data locally. It never collects or uploads prompts, code, or model responses.
337
+
333
338
  | Protection | Description |
334
339
  |---|---|
335
- | **No content upload** | Only token counts and timestamps. Never prompts, responses, or file contents. |
336
- | **Local-only by default** | All data stays on your machine. The leaderboard is fully opt-in. |
337
- | **Auditable** | Open source. Read [`src/lib/rollout.js`](src/lib/rollout.js) — only numbers and timestamps. |
338
- | **Anonymous usage stats only** | Two things phone home, both anonymous: (1) at most one daily heartbeat — a one-way hash of the machine id, plus the app version, OS platform, and app shell (cli/macos/windows/linux) as separate plain fields; (2) anonymous dashboard pageview/feature events (PostHog — autocapture and session recording disabled, browser Do-Not-Track respected). Never token counts, model names, prompts, or paths. Audit [`src/lib/telemetry.js`](src/lib/telemetry.js) and [`dashboard/src/lib/analytics.js`](dashboard/src/lib/analytics.js); one switch disables both on your machine: `TOKENTRACKER_NO_TELEMETRY=1` (or `DO_NOT_TRACK=1`). |
340
+ | **No code or prompt collection** | TokenTracker parses local log files for token counts and timestamps. Prompts, completions, code, and conversation bodies are never saved or uploaded. |
341
+ | **Local-first by default** | Usage tracking runs entirely on your machine. No account, login, or API keys required. Cloud sync and the public leaderboard are strictly opt-in. |
342
+ | **Auditable** | Open source. Check [`src/lib/rollout.js`](src/lib/rollout.js) — only numbers, timestamps, and model names are extracted. |
343
+ | **Transparent network activity** | Fully documented in the [Privacy Policy](docs/PRIVACY.md). Default requests are limited to direct provider quota checks (using credentials already on your machine), GitHub star counts, update checks, pricing data refreshes (`raw.githubusercontent.com`), and anonymous telemetry (daily heartbeat + PostHog pageviews, both disabled with `TOKENTRACKER_NO_TELEMETRY=1` or `DO_NOT_TRACK=1`). Telemetry never includes tokens, models, prompts, or code. |
339
344
 
340
345
  ---
341
346
 
@@ -352,12 +357,14 @@ Most users never need this — defaults are sensible. For advanced setups:
352
357
  | `TOKENTRACKER_GIT_ATTRIBUTION_PROTECTED_DIRS` | Let Git attribution enter macOS TCC-protected locations (`1` to allow). By default sessions under `~/Documents`, `~/Downloads`, `~/Desktop`, `~/Library`, the media folders and `/Volumes` are skipped, because macOS raises a separate folder-access prompt for each one. Enable this only if you keep repositories there and don't mind granting access | — |
353
358
  | `TOKENTRACKER_WSL_MODE` | WSL install resolution behavior on Windows (for aggregating native and WSL installations). `wsl-first` (prefer WSL), `native-first`, `wsl-only`, `native-only`, `both` (aggregate both installs) | `wsl-first` |
354
359
  | `CODEX_HOME` | Override Codex CLI directory | `~/.codex` |
360
+ | `TOKENTRACKER_ACODE_HOME` | Override AStudio directory | `~/.acode` |
355
361
  | `GEMINI_HOME` | Override Gemini CLI directory | `~/.gemini` |
356
362
  | `TOKENTRACKER_GROK_HOME` | Override Grok Build directory for the Grok integration and Skills Manager | `~/.grok` |
357
363
  | `GROK_HOME` | Legacy Grok Build directory override, used when `TOKENTRACKER_GROK_HOME` is unset | `~/.grok` |
358
364
  | `TOKENTRACKER_ANTIGRAVITY_HOME` | Force a single Antigravity Skills directory (auto-detects `~/.gemini/antigravity` + `~/.gemini/antigravity-ide` otherwise) | auto |
359
365
  | `TOKENTRACKER_LMSTUDIO_HOME` | Override the LM Studio data directory used by the passive server-log reader | `~/.lmstudio` |
360
366
  | `TOKENTRACKER_UNSLOTH_DB` | Override the Unsloth Studio database file used by the passive usage reader | `$UNSLOTH_STUDIO_HOME/studio.db` |
367
+ | `TOKENTRACKER_DEVIN_DB` | Override the Devin CLI database file used by the passive usage reader | `$XDG_DATA_HOME/devin/cli/sessions.db` |
361
368
 
362
369
  ### 🐧 Windows Subsystem for Linux (WSL) Auto-Discovery
363
370
 
@@ -375,10 +382,11 @@ Configure this behavior using the `TOKENTRACKER_WSL_MODE` environment variable:
375
382
 
376
383
  Supported providers for WSL auto-discovery and aggregation:
377
384
  * **Dual-Install Aggregation (`both` mode):**
378
- * **File-list & Active CLIs:** Every Code, Kimi (legacy & Code), Gemini CLI, Antigravity, OpenCode (JSON), Codex CLI, CodeBuddy, WorkBuddy, oh-my-pi (omp), pi, GitHub Copilot (OTEL), Roo Code, Craft, Kilo Code, Droid.
385
+ * **File-list & Active CLIs:** Every Code, Kimi (legacy & Code), Gemini CLI, Antigravity, OpenCode (JSON), Codex CLI, AStudio, CodeBuddy, WorkBuddy, oh-my-pi (omp), pi, GitHub Copilot (OTEL), Roo Code, Craft, Kilo Code, Droid.
379
386
  * **SQLite-based DBs:** Hermes, Zed Agent, Goose, OpenCode (`opencode.db`), Kilo CLI, Mimo Code, ZCode, Qoder, Claude Science (`operon-cli.db`), GitHub Copilot (App DB).
380
387
  * **WSL Auto-Discovery (Preference/Isolation only, no dual aggregation):**
381
388
  * Grok Build (dynamically selects either native or WSL depending on the mode, but does not aggregate both in `both` mode).
389
+ * Devin CLI (no native Windows data directory is known, so automatic discovery only ever finds a WSL install; `native-only` mode discovers nothing and `both` never aggregates a native copy). The `TOKENTRACKER_DEVIN_DB` override still points at an explicit database file.
382
390
 
383
391
  ---
384
392
 
package/README.zh-CN.md CHANGED
@@ -6,7 +6,7 @@
6
6
 
7
7
  ### 跨所有 CLI,看清你到底在 AI 上花了多少钱
8
8
 
9
- 自动采集 **36 款 AI 编码工具** 的 token 用量,全程本地聚合,用一套漂亮的 Dashboard 看真实成本与趋势。不需要云账号、不需要 API Key、不需要任何配置 —— 一条命令搞定。
9
+ 自动采集 **38 款 AI 编码工具** 的 token 用量,全程本地聚合,用一套漂亮的 Dashboard 看真实成本与趋势。不需要云账号、不需要 API Key、不需要任何配置 —— 一条命令搞定。
10
10
 
11
11
  [![npm version](https://img.shields.io/npm/v/tokentracker-cli.svg?color=blue)](https://www.npmjs.com/package/tokentracker-cli)
12
12
  [![npm downloads](https://img.shields.io/npm/dm/tokentracker-cli.svg?color=brightgreen)](https://www.npmjs.com/package/tokentracker-cli)
@@ -50,7 +50,7 @@ npx tokentracker-cli
50
50
  **30 秒能看到什么:**
51
51
  - 📊 本地 Dashboard(`localhost:7680`)—— 用量趋势、模型明细、成本分析
52
52
  - 🔌 自动识别并挂接你电脑上所有已安装的 AI 工具
53
- - 🏠 100% 本地运行 —— 无账号、无 API Key、无网络请求(排行榜是可选的)
53
+ - 🏠 本地优先 —— 直接在本地机器解析日志,无需账号或 API Key
54
54
  - 🧩 *可选:* Skills 面板——浏览 250+ 公开 skill,并在 Claude · Codex · Grok · Antigravity · Gemini · OpenCode · Hermes 之间同步
55
55
 
56
56
  > **想要原生桌面 App?**
@@ -90,19 +90,19 @@ brew install xiufengsun/tokentracker/tokentracker
90
90
 
91
91
  ## ✨ 特性
92
92
 
93
- - 🔌 **开箱即用支持 36 款 AI 工具** —— Claude Code、Codex CLI、Cursor、Gemini CLI、Kiro、OpenCode、OpenClaw、Every Code、Hermes Agent、GitHub Copilot、Kimi Code、CodeBuddy、WorkBuddy、Grok Build、oh-my-pi、pi、Dots、Prime Agent、Craft Agents、Reasonix、Kilo CLI、Kilo Code、Roo Code、Antigravity、Zed Agent、Goose、Droid、Mimo Code、ZCode、Qoder、AnythingLLM Desktop、Claude Science、DeepSeek Harness、TRAE Work CN、LM Studio、Unsloth Studio
94
- - 🏠 **100% 本地** —— Token 数据绝不离开你的机器。无账号、无 API Key
93
+ - 🔌 **开箱即用支持 38 款 AI 工具** —— Claude Code、Codex CLI、AStudio、Cursor、Gemini CLI、Kiro、OpenCode、OpenClaw、Every Code、Hermes Agent、GitHub Copilot、Kimi Code、CodeBuddy、WorkBuddy、Grok Build、oh-my-pi、pi、Dots、Prime Agent、Craft Agents、Reasonix、Kilo CLI、Kilo Code、Roo Code、Antigravity、Zed Agent、Goose、Droid、Mimo Code、ZCode、Qoder、AnythingLLM Desktop、Claude Science、DeepSeek Harness、TRAE Work CN、LM Studio、Unsloth Studio、Devin CLI
94
+ - 🏠 **本地优先** —— 跑在本地。直接在本地机器解析日志,无需账号或 API Key。
95
95
  - 🚀 **零配置** —— 首次运行自动安装所有 hook。30 秒从零到 Dashboard
96
96
  - 📊 **漂亮的 Dashboard** —— 用量趋势、按模型的成本分解、GitHub 风格活跃度热力图、按项目归因
97
97
  - 🖥️ **原生桌面 App** —— macOS 菜单栏(含桌面小组件)与 Windows 系统托盘,各自内嵌服务 + 原生 WebView Dashboard
98
98
  - 🎨 **4 种桌面小组件** —— 用量 / 热力图 / 热门模型 / 使用限额 直接钉桌面
99
- - 📈 **实时限额追踪** —— Claude / Codex / Cursor / Gemini / Kimi / Kiro / Grok / Copilot / Antigravity / ZCode / OpenCode Go / Qoder / Qoder CN 的配额窗口;本地 provider App 暂时退出时保留 last-good 缓存
99
+ - 📈 **实时限额追踪** —— Claude / Codex / Cursor / Gemini / Kimi / Kiro / Grok / Copilot / Antigravity / ZCode / OpenCode Go / Qoder / Qoder CN / Command Code / Ark Coding Plan / Ark Agent Plan / Devin 的配额窗口;本地 provider App 暂时退出时保留 last-good 缓存
100
100
  - 🟢 **服务状态页** —— 直接汇总 8 家 provider 官方状态页的实时运行与事故状态
101
101
  - 💰 **成本引擎** —— 内置 70+ 模型定价表,精确到 USD
102
102
  - 🌐 **可选排行榜** —— 与全球开发者对比;列可拖拽排序,聚焦你关心的 provider(需登录参与)
103
- - 🔄 **跨设备账户视图** —— 开启云同步后,Dashboard 会把你在每台机器(笔记本 + 台式机 + 服务器)上的用量合并成一个视图——总量、趋势、热力图、模型明细全部跨设备聚合(可选,需登录;默认的纯本地体验依然即时、离线)
103
+ - 🔄 **跨设备账户视图** —— 开启云同步后,Dashboard 会把你在每台机器(笔记本 + 台式机 + 服务器)上的用量合并成一个视图——总量、趋势、热力图、模型明细全部跨设备聚合(可选,需登录)
104
104
  - 🧩 **可选 Skills 面板** —— 浏览 250+ 公开 skill(来自 `anthropics/skills`、`ComposioHQ/awesome-claude-skills`、`skills.sh` 以及你自己添加的任何 GitHub 仓库),在 Claude / Codex / Grok / Antigravity / Gemini / OpenCode / Hermes 之间同步,目标 Agent 文字可见、可单独管理、一键撤销
105
- - 🔒 **隐私优先** —— 只记录 token 数量与时间戳。**绝不**收集 prompt、回复、文件内容
105
+ - 🔒 **隐私优先** —— 仅记录本地用量指标(token 数量、时间戳与模型名称)。Prompt、模型回复和代码绝不离开你的机器。
106
106
 
107
107
  ---
108
108
 
@@ -144,7 +144,7 @@ brew install xiufengsun/tokentracker/tokentracker
144
144
  <tr>
145
145
  <td colspan="2">
146
146
 
147
- **Skills 管理器** —— 浏览 250+ 公开 skill(GitHub 仓库 & `skills.sh`),一次安装,同步到 Claude / Codex / Grok / Antigravity / Gemini / OpenCode / Hermes。每个 Agent 单独开关,一键撤销,再也不用手动复制文件夹。
147
+ **Skills 管理器** —— 浏览 250+ 公开 skill(GitHub 仓库 & `skills.sh`),一次安装,同步到 Claude / Codex / AStudio / Grok / Antigravity / Gemini / OpenCode / Hermes。每个 Agent 单独开关,一键撤销,再也不用手动复制文件夹。
148
148
 
149
149
  <img src="https://raw.githubusercontent.com/xiufengsun/tokentracker/main/docs/screenshots/skills.png" alt="Skills Manager" />
150
150
 
@@ -169,6 +169,7 @@ brew install xiufengsun/tokentracker/tokentracker
169
169
  |---|---|---|
170
170
  | **Claude Code** | ✅ 自动 | 写入 `settings.json` 的 SessionEnd hook |
171
171
  | **Codex CLI** | ✅ 自动 | 写入 `config.toml` 的 TOML notify hook |
172
+ | **AStudio** | ✅ 自动 | 写入 `config.toml` 的 TOML notify hook |
172
173
  | **Cursor** | ✅ 自动 | API + SQLite 中的 auth token |
173
174
  | **Kiro** | ✅ 自动 | SQLite + JSONL 混合读取 |
174
175
  | **Gemini CLI** | ✅ 自动 | SessionEnd hook |
@@ -201,15 +202,16 @@ brew install xiufengsun/tokentracker/tokentracker
201
202
  | **LM Studio** | ✅ 自动 | 被动递归读取 `~/.lmstudio/server-logs/**/*.log`;只读取 Chat Completions 和 Responses API 最终响应中的 ID、模型、时间戳与标量 `usage` 计数,并按响应 ID 去重,不保留提示词或回复正文。该数据源覆盖本地推理与 LM Link(API 边际成本为 $0),不包含 Bionic Secure Cloud 账单。 |
202
203
  | **Unsloth Studio** | ✅ 自动 | 被动读取 `$UNSLOTH_STUDIO_HOME/studio.db`(默认 `~/.unsloth/studio/studio.db`);只投影标量 `contextUsage` 元数据和不含内容的 `api_usage_events`。不会读取提示词、回复、附件、API subject、凭据或训练指标;本地路由成本为 $0,已知付费 provider 路由按实际响应模型估算 token 成本。 |
203
204
  | **AnythingLLM Desktop** | ✅ 自动 | 被动 SQLite 读取(`anythingllm-desktop/storage/anythingllm.db`;只读取每条消息的 token 指标,不读取 prompt 或回复) |
205
+ | **Devin CLI** (Cognition) | ✅ 自动 | 被动 SQLite 读取(`$XDG_DATA_HOME/devin/cli/sessions.db`,默认 `~/.local/share/devin/cli/sessions.db`;按 `request_id` 对每次请求的用量指标去重——replay/fork/compaction 副本不会重复计数——使用记录的 generation model,不读取 prompt、回复或 `cogs_json`)。Devin 模型(`swe-2`、`swe-2-high`、`compactor`)当前没有定价数据,token 数照常统计但不计入美元估算——显示 $0 并不代表免费。没有已知的原生 Windows 数据目录;WSL 安装通过 `\\wsl$` 读取。 |
204
206
  | **Claude Science** | ✅ 自动 | 被动 SQLite 读取(`~/.claude-science/operon-cli.db`;只读取 `frames` 表的 token 计数,不读取 prompt、产物或研究内容)。没有原生 Windows 版——Windows 上该应用运行在 WSL 内,从 WSL 中读取。 |
205
207
  | **DeepSeek Harness** | ✅ 自动 | 被动读取会话日志(`~/.dsh/sessions/**/session.jsonl[.zstd]`;解析会话头部与 assistant 事件,支持多帧 zstd 解压) |
206
208
  | **TRAE Work CN** | ✅ 自动 | **需要显式开启:设置 `TOKENTRACKER_TRAE_CN_USAGE=1`。** 读取用量会把本地保存的登录授权发送到 TRAE 的内部 API,因此在你开启之前不会发出任何请求。开启后:仅在存在本地 TRAE Work CN 登录授权的可执行非后台同步期间,读取 macOS / Windows 本地登录应用的 session-token 用量;内部 API 可能变化 |
207
209
 
208
210
  > **需要手动装什么插件 / hook 吗?** 不需要。`tokentracker`(或 `tokentracker init`)第一次跑的时候会全部搞定:
209
- > - **基于 hook 的工具**(Claude Code、Codex、Gemini、Every Code、**CodeBuddy**、**WorkBuddy**、**Grok Build**)—— 我们把 SessionEnd hook 或 TOML notify 条目写入它们自己的配置文件
211
+ > - **基于 hook 的工具**(Claude Code、Codex、AStudio、Gemini、Every Code、**CodeBuddy**、**WorkBuddy**、**Grok Build**)—— 我们把 SessionEnd hook 或 TOML notify 条目写入它们自己的配置文件
210
212
  > - **基于插件的工具**(OpenCode、**OpenClaw**)—— 插件随 npm 包一起分发。OpenClaw 的 session plugin 位于 `~/.tokentracker/tracker/openclaw-plugin/openclaw-session-sync/`;TokenTracker 会通过 OpenClaw 自己的 CLI 挂接并启用它,然后写入 `hooks.allowConversationAccess=true`,让 OpenClaw 放行触发同步的会话结束事件。无需下载、无需拖拽
211
213
  > - **oh-my-pi** —— 被动会话扫描始终是计费/token 的事实来源(`~/.omp/agent/sessions/**/*.jsonl`)。检测到 OMP 时,`tokentracker init` 还会写入托管的 notify 扩展到 `~/.omp/agent/extensions/tokentracker-notify.ts`,以便回合结束后近实时触发同步。仅当文件带有 TokenTracker 托管标记时才视为我们拥有:同名用户自写扩展绝不会被覆盖或删除。`tokentracker uninstall` 也仅在文件仍为托管状态时删除它。
212
- > - **被动读取类**(Cursor、Kiro、Hermes、Kimi Code、Copilot、**Grok Build**、**pi**、**Craft Agents**、**Reasonix**、**Kilo CLI**、**Kilo Code**、**Roo Code**、**Antigravity**、**Zed Agent**、**Goose**、**Droid**、**Mimo Code**、**ZCode**、**Qoder**、**LM Studio**、**Unsloth Studio**、**AnythingLLM Desktop**、**Claude Science**、**DeepSeek Harness**) —— 完全不往它们里面塞东西,只读取它们自己产生的文件(SQLite DB、JSONL、OTEL 导出、会话轨迹日志)。Copilot App / CLI 的用量按请求读取 `~/.copilot/session-store.db`;`data.db` 只提供一次旧数据迁移基线,并在 store 成为主源后保持仅观察,Chat 扩展和旧版 CLI 继续使用 OTEL。TokenTracker 会协调这些数据源,使重叠请求只统计一次。迁移前无法无损拆分模型的 App/CLI 混合历史会保留为 `github-copilot-legacy` 聚合量,而不会猜测请求模型
214
+ > - **被动读取类**(Cursor、Kiro、Hermes、Kimi Code、Copilot、**Grok Build**、**pi**、**Craft Agents**、**Reasonix**、**Kilo CLI**、**Kilo Code**、**Roo Code**、**Antigravity**、**Zed Agent**、**Goose**、**Droid**、**Mimo Code**、**ZCode**、**Qoder**、**LM Studio**、**Unsloth Studio**、**AnythingLLM Desktop**、**Devin CLI**、**Claude Science**、**DeepSeek Harness**) —— 完全不往它们里面塞东西,只读取它们自己产生的文件(SQLite DB、JSONL、OTEL 导出、会话轨迹日志)。Copilot App / CLI 的用量按请求读取 `~/.copilot/session-store.db`;`data.db` 只提供一次旧数据迁移基线,并在 store 成为主源后保持仅观察,Chat 扩展和旧版 CLI 继续使用 OTEL。TokenTracker 会协调这些数据源,使重叠请求只统计一次。迁移前无法无损拆分模型的 App/CLI 混合历史会保留为 `github-copilot-legacy` 聚合量,而不会猜测请求模型
213
215
  > - **Grok Build 估算说明** —— Grok 当前本地遥测提供 `updates.jsonl` 里的累计 `totalTokens`,但还没有稳定的输入/输出/cache 拆分;`signals.json` 仍作为 `contextTokensUsed` 快照兜底。所以在 Grok 提供按调用粒度的用量明细之前,TokenTracker 对 Grok 成本仍是估算值
214
216
  >
215
217
  > 任何时候都可以用 `tokentracker status` 查看每个集成的状态。如果显示 `skipped`,`detail` 列会解释原因(例如某工具 CLI 不在 `PATH` 上、config 不可读等)。
@@ -220,15 +222,21 @@ brew install xiufengsun/tokentracker/tokentracker
220
222
 
221
223
  ---
222
224
 
223
- ## 🆚 为什么选 TokenTracker?
225
+ ## 🆚 为什么选 TokenTracker? <a id="ccusage-alternative"></a>
224
226
 
225
- | | **TokenTracker** | ccusage | Cursor 自带统计 |
226
- |--------------------------|:---:|:---:|:---:|
227
- | **支持的 AI 工具数** | **36** | 1(Claude) | 1(Cursor) |
228
- | **本地优先,无需账号** | ✅ | ✅ | ❌ |
229
- | **原生桌面 App** | ✅ macOS + Windows | ❌ | ❌ |
230
- | **桌面小组件** | ✅ 4 个小组件 | ❌ | ❌ |
231
- | **限额追踪** | ✅ 13 家 provider | ❌ | 只支持 Cursor |
227
+ > **想要带桌面小组件和可视化界面的用量追踪体验?** TokenTracker 将面向众多 AI 编码工具的本地优先解析,与原生桌面 App、桌面小组件、多 provider 限额监控以及可选的云同步结合在一起。
228
+
229
+ | 功能维度 | **[TokenTracker](https://github.com/xiufengsun/TokenTracker)** | **[ccusage](https://github.com/ccusage/ccusage)** | **[Tokscale](https://github.com/junhoyeo/tokscale)** |
230
+ |---|:---:|:---:|:---:|
231
+ | **支持的 AI 工具数** | **38** | 多 Agent / 工具支持 | 多 Agent / 工具支持 |
232
+ | **主要交互方式** | 原生桌面 App 与 Web Dashboard | 终端命令行(CLI) | 终端 TUI 与 CLI |
233
+ | **本地优先分析** | ✅ | ✅ | ✅ |
234
+ | **原生桌面 App** | ✅ macOS、Windows、Linux | ❌ | ❌ |
235
+ | **桌面小组件** | ✅ 4 个小组件 | ❌ | ❌ |
236
+ | **限额追踪** | ✅ 17 家 provider | 较少(Claude 周期/用量) | 覆盖多个 provider |
237
+ | **终端数据分析** | 基础 CLI(`status`、`--json`) | 完善的 CLI 报表能力 | 丰富的交互式 TUI 与 CLI |
238
+ | **JSON 导出** | ✅ | ✅ | ✅ |
239
+ | **公开排行榜** | 可选(需主动登录参与) | ❌ | 可选(`submit`) |
232
240
 
233
241
  ---
234
242
 
@@ -236,7 +244,7 @@ brew install xiufengsun/tokentracker/tokentracker
236
244
 
237
245
  ```mermaid
238
246
  flowchart LR
239
- A["AI 编码工具<br/>Claude Code · Codex · Cursor · Gemini · Kiro<br/>OpenCode · OpenClaw · Every Code · Hermes · Copilot<br/>Kimi · CodeBuddy · WorkBuddy · Grok · Kilo · Roo · Zed · Goose<br/>Antigravity · oh-my-pi · pi · Craft · Droid · Mimo · ZCode · Qoder · AnythingLLM · Claude Science · DeepSeek Harness · TRAE Work CN · LM Studio · Unsloth Studio"]
247
+ A["AI 编码工具<br/>Claude Code · Codex · AStudio · Cursor · Gemini · Kiro<br/>OpenCode · OpenClaw · Every Code · Hermes · Copilot<br/>Kimi · CodeBuddy · WorkBuddy · Grok · Kilo · Roo · Zed · Goose<br/>Antigravity · oh-my-pi · pi · Craft · Droid · Mimo · ZCode · Qoder · AnythingLLM · Claude Science · DeepSeek Harness · TRAE Work CN · LM Studio · Unsloth Studio · Devin CLI"]
240
248
  A -->|hook 触发| B[Token Tracker]
241
249
  B -->|解析日志<br/>30 分钟 UTC 桶| C[(本地 SQLite)]
242
250
  C --> D[Web Dashboard]
@@ -247,7 +255,7 @@ flowchart LR
247
255
 
248
256
  1. 正常使用 AI CLI 工具时,它们会写 log
249
257
  2. 轻量级 hook 感知日志变动并触发同步(Cursor 走 API,不走 hook)
250
- 3. token 数量在本地解析 —— **绝不**读取 prompt 或回复内容
258
+ 3. 在本地解析 token 数量 —— Prompt、回复正文和代码绝不保存或上传
251
259
  4. 聚合成 30 分钟一格的 UTC 桶
252
260
  5. Dashboard、菜单栏 App 和桌面小组件都读取同一份本地快照
253
261
 
@@ -257,12 +265,14 @@ flowchart LR
257
265
 
258
266
  > 📄 **[完整隐私政策](docs/PRIVACY.md)**(英文)—— 逐条列出本应用可能发起的每一个网络请求、各自发送了什么,以及如何关闭。
259
267
 
260
- | 承诺 | 说明 |
268
+ TokenTracker 在本地处理你的用量数据。绝不收集或上传 prompt、代码或模型回复。
269
+
270
+ | 保护项 | 说明 |
261
271
  |---|---|
262
- | **不上传内容** | 只记录 token 数量和时间戳。**绝不**记录 prompt、回复或文件内容 |
263
- | **默认纯本地** | 所有数据保留在你的机器上。排行榜是完全 opt-in |
264
- | **可审计** | 代码开源。[`src/lib/rollout.js`](src/lib/rollout.js) 里只有数字和时间戳 |
265
- | **仅匿名使用统计** | 只有两类匿名上报:(1) 每天最多一次的心跳——机器 ID 的单向哈希、应用版本、操作系统平台、运行形态(cli/macos/windows/linux);(2) 匿名的 dashboard 页面/功能事件(PostHog——已关闭 autocapture 和会话录制,尊重浏览器 Do-Not-Track)。绝不包含 token 数量、模型名、prompt 或路径。可在 [`src/lib/telemetry.js`](src/lib/telemetry.js) 与 [`dashboard/src/lib/analytics.js`](dashboard/src/lib/analytics.js) 审计;一个开关同时关闭两者:`TOKENTRACKER_NO_TELEMETRY=1`(或 `DO_NOT_TRACK=1`) |
272
+ | **不收集代码与 Prompt** | TokenTracker 解析本地日志文件中的 token 数量与时间戳。Prompt、回复正文、代码和对话内容绝不保存或上传。 |
273
+ | **默认本地优先** | 用量追踪完全跑在你的本地机器上。无需账号、登录或 API Key。云端同步与公开排行榜完全属于自选(Opt-in)。 |
274
+ | **可公开审计** | 完全开源。欢迎查阅 [`src/lib/rollout.js`](src/lib/rollout.js) —— 仅提取数字、时间戳与模型名称。 |
275
+ | **透明的网络请求** | 完整披露于[隐私政策](docs/PRIVACY.md)。默认发起的网络请求仅限于:直接查询 provider 配额(使用你机器上已有的本地凭证)、获取 GitHub Star 数与检查更新、更新定价数据(`raw.githubusercontent.com`),以及匿名产品遥测(每日一次心跳和 PostHog 页面访问,均可通过 `TOKENTRACKER_NO_TELEMETRY=1` 或 `DO_NOT_TRACK=1` 关闭)。遥测绝不包含 token、模型名、prompt 或代码。 |
266
276
 
267
277
  ---
268
278
 
@@ -278,6 +288,7 @@ flowchart LR
278
288
  | `TOKENTRACKER_DISABLE_GIT_ATTRIBUTION` | 关闭 Git 提交归因(设为 `1` 关闭)。归因会在每个近期会话的工作目录里执行 `git log`。关闭后 TokenTracker 完全不会进入你的项目目录(Outcomes 页只显示手动记录的结果) | — |
279
289
  | `TOKENTRACKER_GIT_ATTRIBUTION_PROTECTED_DIRS` | 允许 Git 归因进入 macOS 受保护目录(设为 `1` 允许)。默认会跳过 `~/Documents`、`~/Downloads`、`~/Desktop`、`~/Library`、影片/音乐/图片以及 `/Volumes` 下的会话,因为 macOS 会为每个位置单独弹一次访问授权提示。只有当你确实把仓库放在这些目录、且不介意授权时才需要开启 | — |
280
290
  | `CODEX_HOME` | 覆盖 Codex CLI 目录 | `~/.codex` |
291
+ | `TOKENTRACKER_ACODE_HOME` | 覆盖 AStudio 目录 | `~/.acode` |
281
292
  | `GEMINI_HOME` | 覆盖 Gemini CLI 目录 | `~/.gemini` |
282
293
  | `TOKENTRACKER_GROK_HOME` | 覆盖 Grok Build 目录,供 Grok 集成和 Skills Manager 使用 | `~/.grok` |
283
294
  | `GROK_HOME` | 旧版 Grok Build 目录覆盖变量;未设置 `TOKENTRACKER_GROK_HOME` 时使用 | `~/.grok` |
@@ -1 +1 @@
1
- import{m as o,d as N,j as r}from"./__vite-browser-external-BGnq8Ebm.js";import{Z as $}from"./zap-DStzvQJI.js";import{F as A}from"./flame-B_X-7VNp.js";const j=[["path",{d:"M10 22V7a1 1 0 0 0-1-1H4a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-5a1 1 0 0 0-1-1H2",key:"1ah6g2"}],["rect",{x:"14",y:"2",width:"8",height:"8",rx:"1",key:"88lufb"}]],E=o("blocks",j);const L=[["path",{d:"M12 18V5",key:"adv99a"}],["path",{d:"M15 13a4.17 4.17 0 0 1-3-4 4.17 4.17 0 0 1-3 4",key:"1e3is1"}],["path",{d:"M17.598 6.5A3 3 0 1 0 12 5a3 3 0 1 0-5.598 1.5",key:"1gqd8o"}],["path",{d:"M17.997 5.125a4 4 0 0 1 2.526 5.77",key:"iwvgf7"}],["path",{d:"M18 18a4 4 0 0 0 2-7.464",key:"efp6ie"}],["path",{d:"M19.967 17.483A4 4 0 1 1 12 18a4 4 0 1 1-7.967-.517",key:"1gq6am"}],["path",{d:"M6 18a4 4 0 0 1-2-7.464",key:"k1g0md"}],["path",{d:"M6.003 5.125a4 4 0 0 0-2.526 5.77",key:"q97ue3"}]],C=o("brain",L);const T=[["path",{d:"M8 2v3",key:"1ioesn"}],["path",{d:"M16 2v3",key:"otl347"}],["rect",{x:"3",y:"3",width:"18",height:"18",rx:"2",key:"h1oib"}],["path",{d:"M3 9h18",key:"1pudct"}],["path",{d:"M8 13h.01",key:"1sbv64"}],["path",{d:"M12 13h.01",key:"y0uutt"}],["path",{d:"M16 13h.01",key:"wip0gl"}],["path",{d:"M8 17h.01",key:"p3bg7i"}],["path",{d:"M12 17h.01",key:"p32p05"}],["path",{d:"M16 17h.01",key:"ql8jdd"}]],q=o("calendar-days",T);const z=[["path",{d:"M11.562 3.266a.5.5 0 0 1 .876 0L15.39 8.87a1 1 0 0 0 1.516.294L21.183 5.5a.5.5 0 0 1 .798.519l-2.834 10.246a1 1 0 0 1-.956.734H5.81a1 1 0 0 1-.957-.734L2.02 6.02a.5.5 0 0 1 .798-.519l4.276 3.664a1 1 0 0 0 1.516-.294z",key:"1vdc57"}],["path",{d:"M5 21h14",key:"11awu3"}]],B=o("crown",z);const H=[["path",{d:"M14.086 18.412A2 2 0 0112.67 19H5v-7.672a2 2 0 01.586-1.414L11.75 3.75a6 6 0 118.49 8.49z",key:"1nq9jb"}],["path",{d:"M16 8 2 22",key:"vp34q"}],["path",{d:"M17.488 15H9",key:"16yirz"}]],R=o("feather",H);const D=[["path",{d:"M18 19a5 5 0 0 1-5-5v8",key:"sz5oeg"}],["path",{d:"M9 20H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h3.9a2 2 0 0 1 1.69.9l.81 1.2a2 2 0 0 0 1.67.9H20a2 2 0 0 1 2 2v5",key:"1w6njk"}],["circle",{cx:"13",cy:"12",r:"2",key:"1j92g6"}],["circle",{cx:"20",cy:"19",r:"2",key:"1obnsp"}]],F=o("folder-git-2",D);const I=[["path",{d:"M4 16v-2.38C4 11.5 2.97 10.5 3 8c.03-2.72 1.49-6 4.5-6C9.37 2 10 3.8 10 5.5c0 3.11-2 5.66-2 8.68V16a2 2 0 1 1-4 0Z",key:"1dudjm"}],["path",{d:"M20 20v-2.38c0-2.12 1.03-3.12 1-5.62-.03-2.72-1.49-6-4.5-6C14.63 6 14 7.8 14 9.5c0 3.11 2 5.66 2 8.68V20a2 2 0 1 0 4 0Z",key:"l2t8xc"}],["path",{d:"M16 17h4",key:"1dejxt"}],["path",{d:"M4 13h4",key:"1bwh8b"}]],G=o("footprints",I);const S=[["path",{d:"M2 9.5a5.5 5.5 0 0 1 9.591-3.676.56.56 0 0 0 .818 0A5.49 5.49 0 0 1 22 9.5c0 2.29-1.5 4-3 5.5l-5.492 5.313a2 2 0 0 1-3 .019L5 15c-1.5-1.5-3-3.2-3-5.5",key:"mvr1a0"}]],V=o("heart",S);const Z=[["path",{d:"M7.21 15 2.66 7.14a2 2 0 0 1 .13-2.2L4.4 2.8A2 2 0 0 1 6 2h12a2 2 0 0 1 1.6.8l1.6 2.14a2 2 0 0 1 .14 2.2L16.79 15",key:"143lza"}],["path",{d:"M11 12 5.12 2.2",key:"qhuxz6"}],["path",{d:"m13 12 5.88-9.8",key:"hbye0f"}],["path",{d:"M8 7h8",key:"i86dvs"}],["circle",{cx:"12",cy:"17",r:"5",key:"qbz8iq"}],["path",{d:"M12 18v-2h-.5",key:"fawc4q"}]],O=o("medal",Z);const P=[["path",{d:"M18 5h4",key:"1lhgn2"}],["path",{d:"M20 3v4",key:"1olli1"}],["path",{d:"M20.985 12.486a9 9 0 1 1-9.473-9.472c.405-.022.617.46.402.803a6 6 0 0 0 8.268 8.268c.344-.215.825-.004.803.401",key:"kfwtm"}]],X=o("moon-star",P);const K=[["path",{d:"M12 15v5s3.03-.55 4-2c1.08-1.62 0-5 0-5",key:"qeys4"}],["path",{d:"M4.5 16.5c-1.5 1.26-2 5-2 5s3.74-.5 5-2c.71-.84.7-2.13-.09-2.91a2.18 2.18 0 0 0-2.91-.09",key:"u4xsad"}],["path",{d:"M9 12a22 22 0 0 1 2-3.95A12.88 12.88 0 0 1 22 2c0 2.72-.78 7.5-6 11a22.4 22.4 0 0 1-4 2z",key:"676m9"}],["path",{d:"M9 12H4s.55-3.03 2-4c1.62-1.08 5 .05 5 .05",key:"92ym6u"}]],U=o("rocket",K);const W=[["path",{d:"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z",key:"oel41y"}],["path",{d:"m9 12 2 2 4-4",key:"dzmm74"}]],Y=o("shield-check",W);const J=[["path",{d:"M16 7h6v6",key:"box55l"}],["path",{d:"m22 7-8.5 8.5-5-5L2 17",key:"1t1m79"}]],Q=o("trending-up",J),k=[{id:"token_titan",scope:"cloud",icon:B,format:"tokens",art:"/achievements/token-titan.png"},{id:"big_day",scope:"cloud",icon:$,format:"tokens",art:"/achievements/big-day.png"},{id:"wordsmith",scope:"cloud",icon:R,format:"tokens",art:"/achievements/wordsmith.png"},{id:"marathoner",scope:"cloud",icon:G,format:"days",art:"/achievements/marathoner.png"},{id:"streak",scope:"cloud",icon:A,format:"days",art:"/achievements/streak.png"},{id:"weekend_warrior",scope:"cloud",icon:q,format:"days",art:"/achievements/weekend-warrior.png"},{id:"momentum",scope:"cloud",icon:Q,format:"multiplier",art:"/achievements/momentum.png"},{id:"polyglot",scope:"cloud",icon:C,format:"count",art:"/achievements/polyglot.png"},{id:"trendsetter",scope:"cloud",icon:U,format:"count",art:"/achievements/trendsetter.png"},{id:"multitool",scope:"cloud",icon:E,format:"count",art:"/achievements/multitool.png"},{id:"podium",scope:"cloud",icon:O,format:"rank",art:"/achievements/podium.png"},{id:"veteran",scope:"cloud",icon:Y,format:"days",art:"/achievements/veteran.png"},{id:"project_hopper",scope:"local",icon:F,format:"count",art:"/achievements/project-hopper.png"},{id:"project_devotion",scope:"local",icon:V,format:"tokens",art:"/achievements/project-devotion.png"},{id:"night_owl",scope:"local",icon:X,format:"count",art:"/achievements/night-owl.png"}],u=new Map(k.map(e=>[e.id,e])),m=new Map(k.map((e,a)=>[e.id,a]));function de(e,a){return`achievements.badge.${e}.${a}`}function ee(e){return[...e||[]].sort((a,t)=>{const c=(t?.tier||0)-(a?.tier||0);return c!==0?c:(m.get(a?.id)??99)-(m.get(t?.id)??99)})}function re(e){const a=(e||[]).filter(t=>t&&(t.tier||0)>=1&&u.has(t.id));return a.length===0?null:ee(a)[0]}const v={1:"bronze",2:"silver",3:"gold",4:"diamond"},ie=["bronze","silver","gold","diamond"],ae={bronze:{rim:["#a06a44","#5c3a22"],face:["#d29a6c","#96602f"],glyph:"#3f2817",ring:"#e8c3a0",sheen:"rgba(255, 236, 220, 0.45)",label:"#a06a44"},silver:{rim:["#aab3bb","#67707b"],face:["#dde2e7","#9ba5ae"],glyph:"#39424b",ring:"#f2f5f7",sheen:"rgba(255, 255, 255, 0.55)",label:"#8b959f"},gold:{rim:["#c9971c","#8a6508"],face:["#f2ca52","#cf9c26"],glyph:"#5c430e",ring:"#ffe9a8",sheen:"rgba(255, 248, 214, 0.55)",label:"#b8860b"},diamond:{rim:["#62b1e0","#7c6fd9"],face:["#c8ecf6","#84b4ea"],glyph:"#27496d",ring:"#eafcff",sheen:"rgba(255, 255, 255, 0.65)",label:"#5a9bd8"}},y={rim:["#a8ada9","#7c817d"],face:["#cdd2ce","#a3a8a4"],glyph:"#6a706c",ring:"#dde1de",sheen:"rgba(255, 255, 255, 0.25)",label:"#8b908c"};function te(e,a=!1){return a||!e||e<1?y:ae[v[Math.min(e,4)]]||y}function se(e){return v[e]||null}const g={xs:18,sm:24,md:44,xl:88,lg:108},f={xs:.75,sm:1,md:1.5,xl:2,lg:2};function le({badgeId:e,tier:a=0,size:t="md",locked:c=!1,label:i,className:M=""}){const s=u.get(e),h=c||!a||a<1,n=te(a,h),d=t==="fill",l=d?"100%":g[t]||g.md,b=d?4:f[t]||f.md,[_,x]=N.useState(!1),p=s?.icon||null,w=!!s?.art&&!_;return r.jsx("span",{role:i?"img":void 0,"aria-label":i||void 0,"aria-hidden":i?void 0:!0,className:`inline-flex shrink-0 rounded-full ${M}`,style:{width:l,height:l,padding:b,background:`linear-gradient(135deg, ${n.rim[0]} 0%, ${n.ring} 42%, ${n.rim[1]} 100%)`,boxShadow:d||t==="md"||t==="xl"||t==="lg"?"0 0 0 1px rgba(10, 15, 12, 0.06), 0 1px 2px rgba(10, 15, 12, 0.08)":void 0},children:w?r.jsx("img",{src:s.art,alt:"",draggable:!1,loading:"lazy",onError:()=>x(!0),className:"h-full w-full rounded-full object-cover",style:h?{filter:"grayscale(1)",opacity:.55}:void 0}):r.jsx("span",{className:"flex h-full w-full items-center justify-center rounded-full",style:{background:`linear-gradient(145deg, ${n.face[0]}, ${n.face[1]})`},children:p?r.jsx(p,{size:d?64:Math.round(l*.5),color:n.glyph,strokeWidth:2}):null})})}export{le as A,k as B,V as H,Q as T,ae as a,de as b,u as c,ie as d,re as h,te as p,ee as s,se as t};
1
+ import{m as o,d as N,j as r}from"./__vite-browser-external-BDkeh90N.js";import{Z as $}from"./zap-CEq2616W.js";import{F as A}from"./flame-BamzyXzF.js";const j=[["path",{d:"M10 22V7a1 1 0 0 0-1-1H4a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-5a1 1 0 0 0-1-1H2",key:"1ah6g2"}],["rect",{x:"14",y:"2",width:"8",height:"8",rx:"1",key:"88lufb"}]],E=o("blocks",j);const L=[["path",{d:"M12 18V5",key:"adv99a"}],["path",{d:"M15 13a4.17 4.17 0 0 1-3-4 4.17 4.17 0 0 1-3 4",key:"1e3is1"}],["path",{d:"M17.598 6.5A3 3 0 1 0 12 5a3 3 0 1 0-5.598 1.5",key:"1gqd8o"}],["path",{d:"M17.997 5.125a4 4 0 0 1 2.526 5.77",key:"iwvgf7"}],["path",{d:"M18 18a4 4 0 0 0 2-7.464",key:"efp6ie"}],["path",{d:"M19.967 17.483A4 4 0 1 1 12 18a4 4 0 1 1-7.967-.517",key:"1gq6am"}],["path",{d:"M6 18a4 4 0 0 1-2-7.464",key:"k1g0md"}],["path",{d:"M6.003 5.125a4 4 0 0 0-2.526 5.77",key:"q97ue3"}]],C=o("brain",L);const T=[["path",{d:"M8 2v3",key:"1ioesn"}],["path",{d:"M16 2v3",key:"otl347"}],["rect",{x:"3",y:"3",width:"18",height:"18",rx:"2",key:"h1oib"}],["path",{d:"M3 9h18",key:"1pudct"}],["path",{d:"M8 13h.01",key:"1sbv64"}],["path",{d:"M12 13h.01",key:"y0uutt"}],["path",{d:"M16 13h.01",key:"wip0gl"}],["path",{d:"M8 17h.01",key:"p3bg7i"}],["path",{d:"M12 17h.01",key:"p32p05"}],["path",{d:"M16 17h.01",key:"ql8jdd"}]],q=o("calendar-days",T);const z=[["path",{d:"M11.562 3.266a.5.5 0 0 1 .876 0L15.39 8.87a1 1 0 0 0 1.516.294L21.183 5.5a.5.5 0 0 1 .798.519l-2.834 10.246a1 1 0 0 1-.956.734H5.81a1 1 0 0 1-.957-.734L2.02 6.02a.5.5 0 0 1 .798-.519l4.276 3.664a1 1 0 0 0 1.516-.294z",key:"1vdc57"}],["path",{d:"M5 21h14",key:"11awu3"}]],B=o("crown",z);const H=[["path",{d:"M14.086 18.412A2 2 0 0112.67 19H5v-7.672a2 2 0 01.586-1.414L11.75 3.75a6 6 0 118.49 8.49z",key:"1nq9jb"}],["path",{d:"M16 8 2 22",key:"vp34q"}],["path",{d:"M17.488 15H9",key:"16yirz"}]],R=o("feather",H);const D=[["path",{d:"M18 19a5 5 0 0 1-5-5v8",key:"sz5oeg"}],["path",{d:"M9 20H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h3.9a2 2 0 0 1 1.69.9l.81 1.2a2 2 0 0 0 1.67.9H20a2 2 0 0 1 2 2v5",key:"1w6njk"}],["circle",{cx:"13",cy:"12",r:"2",key:"1j92g6"}],["circle",{cx:"20",cy:"19",r:"2",key:"1obnsp"}]],F=o("folder-git-2",D);const I=[["path",{d:"M4 16v-2.38C4 11.5 2.97 10.5 3 8c.03-2.72 1.49-6 4.5-6C9.37 2 10 3.8 10 5.5c0 3.11-2 5.66-2 8.68V16a2 2 0 1 1-4 0Z",key:"1dudjm"}],["path",{d:"M20 20v-2.38c0-2.12 1.03-3.12 1-5.62-.03-2.72-1.49-6-4.5-6C14.63 6 14 7.8 14 9.5c0 3.11 2 5.66 2 8.68V20a2 2 0 1 0 4 0Z",key:"l2t8xc"}],["path",{d:"M16 17h4",key:"1dejxt"}],["path",{d:"M4 13h4",key:"1bwh8b"}]],G=o("footprints",I);const S=[["path",{d:"M2 9.5a5.5 5.5 0 0 1 9.591-3.676.56.56 0 0 0 .818 0A5.49 5.49 0 0 1 22 9.5c0 2.29-1.5 4-3 5.5l-5.492 5.313a2 2 0 0 1-3 .019L5 15c-1.5-1.5-3-3.2-3-5.5",key:"mvr1a0"}]],V=o("heart",S);const Z=[["path",{d:"M7.21 15 2.66 7.14a2 2 0 0 1 .13-2.2L4.4 2.8A2 2 0 0 1 6 2h12a2 2 0 0 1 1.6.8l1.6 2.14a2 2 0 0 1 .14 2.2L16.79 15",key:"143lza"}],["path",{d:"M11 12 5.12 2.2",key:"qhuxz6"}],["path",{d:"m13 12 5.88-9.8",key:"hbye0f"}],["path",{d:"M8 7h8",key:"i86dvs"}],["circle",{cx:"12",cy:"17",r:"5",key:"qbz8iq"}],["path",{d:"M12 18v-2h-.5",key:"fawc4q"}]],O=o("medal",Z);const P=[["path",{d:"M18 5h4",key:"1lhgn2"}],["path",{d:"M20 3v4",key:"1olli1"}],["path",{d:"M20.985 12.486a9 9 0 1 1-9.473-9.472c.405-.022.617.46.402.803a6 6 0 0 0 8.268 8.268c.344-.215.825-.004.803.401",key:"kfwtm"}]],X=o("moon-star",P);const K=[["path",{d:"M12 15v5s3.03-.55 4-2c1.08-1.62 0-5 0-5",key:"qeys4"}],["path",{d:"M4.5 16.5c-1.5 1.26-2 5-2 5s3.74-.5 5-2c.71-.84.7-2.13-.09-2.91a2.18 2.18 0 0 0-2.91-.09",key:"u4xsad"}],["path",{d:"M9 12a22 22 0 0 1 2-3.95A12.88 12.88 0 0 1 22 2c0 2.72-.78 7.5-6 11a22.4 22.4 0 0 1-4 2z",key:"676m9"}],["path",{d:"M9 12H4s.55-3.03 2-4c1.62-1.08 5 .05 5 .05",key:"92ym6u"}]],U=o("rocket",K);const W=[["path",{d:"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z",key:"oel41y"}],["path",{d:"m9 12 2 2 4-4",key:"dzmm74"}]],Y=o("shield-check",W);const J=[["path",{d:"M16 7h6v6",key:"box55l"}],["path",{d:"m22 7-8.5 8.5-5-5L2 17",key:"1t1m79"}]],Q=o("trending-up",J),k=[{id:"token_titan",scope:"cloud",icon:B,format:"tokens",art:"/achievements/token-titan.png"},{id:"big_day",scope:"cloud",icon:$,format:"tokens",art:"/achievements/big-day.png"},{id:"wordsmith",scope:"cloud",icon:R,format:"tokens",art:"/achievements/wordsmith.png"},{id:"marathoner",scope:"cloud",icon:G,format:"days",art:"/achievements/marathoner.png"},{id:"streak",scope:"cloud",icon:A,format:"days",art:"/achievements/streak.png"},{id:"weekend_warrior",scope:"cloud",icon:q,format:"days",art:"/achievements/weekend-warrior.png"},{id:"momentum",scope:"cloud",icon:Q,format:"multiplier",art:"/achievements/momentum.png"},{id:"polyglot",scope:"cloud",icon:C,format:"count",art:"/achievements/polyglot.png"},{id:"trendsetter",scope:"cloud",icon:U,format:"count",art:"/achievements/trendsetter.png"},{id:"multitool",scope:"cloud",icon:E,format:"count",art:"/achievements/multitool.png"},{id:"podium",scope:"cloud",icon:O,format:"rank",art:"/achievements/podium.png"},{id:"veteran",scope:"cloud",icon:Y,format:"days",art:"/achievements/veteran.png"},{id:"project_hopper",scope:"local",icon:F,format:"count",art:"/achievements/project-hopper.png"},{id:"project_devotion",scope:"local",icon:V,format:"tokens",art:"/achievements/project-devotion.png"},{id:"night_owl",scope:"local",icon:X,format:"count",art:"/achievements/night-owl.png"}],u=new Map(k.map(e=>[e.id,e])),m=new Map(k.map((e,a)=>[e.id,a]));function de(e,a){return`achievements.badge.${e}.${a}`}function ee(e){return[...e||[]].sort((a,t)=>{const c=(t?.tier||0)-(a?.tier||0);return c!==0?c:(m.get(a?.id)??99)-(m.get(t?.id)??99)})}function re(e){const a=(e||[]).filter(t=>t&&(t.tier||0)>=1&&u.has(t.id));return a.length===0?null:ee(a)[0]}const v={1:"bronze",2:"silver",3:"gold",4:"diamond"},ie=["bronze","silver","gold","diamond"],ae={bronze:{rim:["#a06a44","#5c3a22"],face:["#d29a6c","#96602f"],glyph:"#3f2817",ring:"#e8c3a0",sheen:"rgba(255, 236, 220, 0.45)",label:"#a06a44"},silver:{rim:["#aab3bb","#67707b"],face:["#dde2e7","#9ba5ae"],glyph:"#39424b",ring:"#f2f5f7",sheen:"rgba(255, 255, 255, 0.55)",label:"#8b959f"},gold:{rim:["#c9971c","#8a6508"],face:["#f2ca52","#cf9c26"],glyph:"#5c430e",ring:"#ffe9a8",sheen:"rgba(255, 248, 214, 0.55)",label:"#b8860b"},diamond:{rim:["#62b1e0","#7c6fd9"],face:["#c8ecf6","#84b4ea"],glyph:"#27496d",ring:"#eafcff",sheen:"rgba(255, 255, 255, 0.65)",label:"#5a9bd8"}},y={rim:["#a8ada9","#7c817d"],face:["#cdd2ce","#a3a8a4"],glyph:"#6a706c",ring:"#dde1de",sheen:"rgba(255, 255, 255, 0.25)",label:"#8b908c"};function te(e,a=!1){return a||!e||e<1?y:ae[v[Math.min(e,4)]]||y}function se(e){return v[e]||null}const g={xs:18,sm:24,md:44,xl:88,lg:108},f={xs:.75,sm:1,md:1.5,xl:2,lg:2};function le({badgeId:e,tier:a=0,size:t="md",locked:c=!1,label:i,className:M=""}){const s=u.get(e),h=c||!a||a<1,n=te(a,h),d=t==="fill",l=d?"100%":g[t]||g.md,b=d?4:f[t]||f.md,[_,x]=N.useState(!1),p=s?.icon||null,w=!!s?.art&&!_;return r.jsx("span",{role:i?"img":void 0,"aria-label":i||void 0,"aria-hidden":i?void 0:!0,className:`inline-flex shrink-0 rounded-full ${M}`,style:{width:l,height:l,padding:b,background:`linear-gradient(135deg, ${n.rim[0]} 0%, ${n.ring} 42%, ${n.rim[1]} 100%)`,boxShadow:d||t==="md"||t==="xl"||t==="lg"?"0 0 0 1px rgba(10, 15, 12, 0.06), 0 1px 2px rgba(10, 15, 12, 0.08)":void 0},children:w?r.jsx("img",{src:s.art,alt:"",draggable:!1,loading:"lazy",onError:()=>x(!0),className:"h-full w-full rounded-full object-cover",style:h?{filter:"grayscale(1)",opacity:.55}:void 0}):r.jsx("span",{className:"flex h-full w-full items-center justify-center rounded-full",style:{background:`linear-gradient(145deg, ${n.face[0]}, ${n.face[1]})`},children:p?r.jsx(p,{size:d?64:Math.round(l*.5),color:n.glyph,strokeWidth:2}):null})})}export{le as A,k as B,V as H,Q as T,ae as a,de as b,u as c,ie as d,re as h,te as p,ee as s,se as t};
@@ -1,2 +1,2 @@
1
- const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/BadgeDetailModal-DGiKdZfz.js","assets/__vite-browser-external-BGnq8Ebm.js","assets/__vite-browser-external-CvMMIjhi.css","assets/react-yHv9MTUA.js","assets/AchievementBadge-DKyElm0T.js","assets/zap-DStzvQJI.js","assets/flame-B_X-7VNp.js","assets/use-transform-Cy1ykjEs.js","assets/use-reduced-motion-DGXcZBX3.js","assets/AchievementsSection-DN69LzXX.js","assets/format-tokens-DEx2TJj8.js","assets/DialogTitle-_ahf0z8b.js","assets/composite-Dxmivlk4.js","assets/store-oQZR4d9M.js"])))=>i.map(i=>d[i]);
2
- import{r as l,G as h,H as f,I as v,j as e,c,L as p,d as g,a as y,J as b,K as k,_ as j}from"./__vite-browser-external-BGnq8Ebm.js";import{A as w}from"./AchievementsSection-DN69LzXX.js";import{B as x}from"./AchievementBadge-DKyElm0T.js";import"./react-yHv9MTUA.js";import"./format-tokens-DEx2TJj8.js";import"./use-reduced-motion-DGXcZBX3.js";import"./zap-DStzvQJI.js";import"./flame-B_X-7VNp.js";function N({enabled:a=!0}={}){const[n,t]=l.useState({status:"loading",achievements:[],error:null}),s=l.useMemo(()=>h(),[]);return l.useEffect(()=>{if(!a){t({status:"disabled",achievements:[],error:null});return}let i=!1;return t(r=>({...r,status:"loading"})),f({timeZone:s,tzOffsetMinutes:v()}).then(r=>{i||t({status:"ready",achievements:Array.isArray(r?.achievements)?r.achievements:[],error:null})}).catch(r=>{i||t({status:"error",achievements:[],error:r})}),()=>{i=!0}},[a,s]),n}const A=g.lazy(()=>j(()=>import("./BadgeDetailModal-DGiKdZfz.js"),__vite__mapDeps([0,1,2,3,4,5,6,7,8,9,10,11,12,13])));function I({authLoading:a,authEnabled:n,authUserId:t,mockEnabled:s}){if(s)return{authLoading:!1,signedIn:!0,userId:"mock-user"};const i=!!(n&&t);return{authLoading:!!a,signedIn:i,userId:i?t:null}}function B(){const a=y(),[n,t]=l.useState({status:"loading",achievements:[],userId:null}),s=b(),{authLoading:i,signedIn:r,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(!r){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,r,o]),{...n,signedIn:r,settled:_({authLoading:i,signedIn:r,userId:o,state:n})}}function _({authLoading:a,signedIn:n,userId:t,state:s}){return a?!1:n?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:x.length}).map((a,n)=>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"})]},n))})}function D(){const a=N(),n=B(),[t,s]=l.useState(null),i=a.status==="loading"||!n.settled,r=l.useMemo(()=>[...n.achievements||[],...a.achievements||[]],[n.achievements,a.achievements]),o=l.useMemo(()=>r.filter(u=>(u?.tier||0)>=1).length,[r]),d=x.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)}%`}})})]})]}),!n.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:r,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/xiufengsun/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,D as default,I as resolveCloudBadgeIdentity};
1
+ const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/BadgeDetailModal-DxoxXst2.js","assets/__vite-browser-external-BDkeh90N.js","assets/__vite-browser-external-5P7me0Qq.css","assets/react-BmFZKA_b.js","assets/AchievementBadge-BX4iBBYM.js","assets/zap-CEq2616W.js","assets/flame-BamzyXzF.js","assets/use-transform-D42P7jkd.js","assets/use-reduced-motion-BqDKim_3.js","assets/AchievementsSection-BM_JWWhJ.js","assets/format-tokens-C4G-_xYt.js","assets/DialogTitle-DL5nvD31.js","assets/composite-BIgEqWZD.js","assets/store-JM-a8ElZ.js"])))=>i.map(i=>d[i]);
2
+ import{r as l,z as h,A as f,F as v,j as e,c,L as p,d as g,a as y,G as b,H as k,_ as j}from"./__vite-browser-external-BDkeh90N.js";import{A as w}from"./AchievementsSection-BM_JWWhJ.js";import{B as x}from"./AchievementBadge-BX4iBBYM.js";import"./react-BmFZKA_b.js";import"./format-tokens-C4G-_xYt.js";import"./use-reduced-motion-BqDKim_3.js";import"./zap-CEq2616W.js";import"./flame-BamzyXzF.js";function N({enabled:a=!0}={}){const[n,t]=l.useState({status:"loading",achievements:[],error:null}),s=l.useMemo(()=>h(),[]);return l.useEffect(()=>{if(!a){t({status:"disabled",achievements:[],error:null});return}let i=!1;return t(r=>({...r,status:"loading"})),f({timeZone:s,tzOffsetMinutes:v()}).then(r=>{i||t({status:"ready",achievements:Array.isArray(r?.achievements)?r.achievements:[],error:null})}).catch(r=>{i||t({status:"error",achievements:[],error:r})}),()=>{i=!0}},[a,s]),n}const A=g.lazy(()=>j(()=>import("./BadgeDetailModal-DxoxXst2.js"),__vite__mapDeps([0,1,2,3,4,5,6,7,8,9,10,11,12,13])));function I({authLoading:a,authEnabled:n,authUserId:t,mockEnabled:s}){if(s)return{authLoading:!1,signedIn:!0,userId:"mock-user"};const i=!!(n&&t);return{authLoading:!!a,signedIn:i,userId:i?t:null}}function B(){const a=y(),[n,t]=l.useState({status:"loading",achievements:[],userId:null}),s=b(),{authLoading:i,signedIn:r,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(!r){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,r,o]),{...n,signedIn:r,settled:_({authLoading:i,signedIn:r,userId:o,state:n})}}function _({authLoading:a,signedIn:n,userId:t,state:s}){return a?!1:n?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:x.length}).map((a,n)=>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"})]},n))})}function D(){const a=N(),n=B(),[t,s]=l.useState(null),i=a.status==="loading"||!n.settled,r=l.useMemo(()=>[...n.achievements||[],...a.achievements||[]],[n.achievements,a.achievements]),o=l.useMemo(()=>r.filter(u=>(u?.tier||0)>=1).length,[r]),d=x.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)}%`}})})]})]}),!n.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:r,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/xiufengsun/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,D as default,I as resolveCloudBadgeIdentity};
@@ -1 +1 @@
1
- import{m as _,j as s,c as l}from"./__vite-browser-external-BGnq8Ebm.js";import{m as $}from"./react-yHv9MTUA.js";import{B as x,s as b,b as C,t as T,A as B,a as E}from"./AchievementBadge-DKyElm0T.js";import{f as L}from"./format-tokens-DEx2TJj8.js";import{u as I}from"./use-reduced-motion-DGXcZBX3.js";const D=[["path",{d:"M10 16h.01",key:"1bzywj"}],["path",{d:"M2.212 11.577a2 2 0 0 0-.212.896V18a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-5.527a2 2 0 0 0-.212-.896L18.55 5.11A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z",key:"18tbho"}],["path",{d:"M21.946 12.013H2.054",key:"zqlbp7"}],["path",{d:"M6 16h.01",key:"1pmjb7"}]],F=_("hard-drive",D);function V(r,i){const t=Number(i)||0;switch(r){case"tokens":return L(t);case"rank":return`#${Math.round(t)}`;case"multiplier":return`${(Math.round(t*10)/10).toString()}×`;default:return Math.round(t).toLocaleString()}}function H(r){if(!r)return 0;const i=Number(r.next_threshold),t=Number(r.metric_value??r.value);if(!Number.isFinite(i)||i<=0)return r.tier>=4?1:0;if(!Number.isFinite(t)||t<=0)return 0;const o=r.lower_is_better?i/t:t/i;return Math.max(0,Math.min(1,o))}function q(r){const i=r?.achieved||{},t=Object.values(i).map(o=>o?Date.parse(o):NaN).filter(o=>Number.isFinite(o));return t.length>0?Math.max(...t):null}function K({achievements:r,isOwn:i=!1,scope:t,onSelect:o,size:m="xl",columns:v="default",animateIn:y=!1,className:N=""}){const k=I(),u=y&&!k,p=new Map;for(const e of Array.isArray(r)?r:[])e&&e.id&&p.set(e.id,e);const g=t?x.filter(e=>e.scope===t):x;let c;if(i){const e=g.map(a=>({entry:a,record:p.get(a.id)||{id:a.id,tier:0}})),n=b(e.filter(a=>(a.record.tier||0)>=1).map(a=>({...a,id:a.entry.id,tier:a.record.tier}))),h=e.filter(a=>(a.record.tier||0)<1);c=[...n,...h]}else c=b(g.map(e=>({entry:e,record:p.get(e.id)})).filter(e=>e.record&&(e.record.tier||0)>=1).map(e=>({...e,id:e.entry.id,tier:e.record.tier}))).map(({entry:e,record:n})=>({entry:e,record:n}));if(c.length===0)return null;const j=v==="wide"?"grid-cols-[repeat(3,max-content)] sm:grid-cols-[repeat(4,max-content)] lg:grid-cols-[repeat(5,max-content)]":"grid-cols-[repeat(3,max-content)] sm:grid-cols-[repeat(4,max-content)]",w=m==="lg"?"mx-0 sm:-mx-2":"-mx-2 sm:-mx-4",M=m==="lg"?"px-0":"px-1";return s.jsx("div",{className:`${w} grid justify-between gap-y-7 ${j} ${N}`,children:c.map(({entry:e,record:n},h)=>{const a=(n.tier||0)>=1,f=l(C(e.id,"name")),d=a?T(n.tier):null,A=H(n);return s.jsxs($.button,{type:"button",initial:u?{opacity:0,y:16,scale:.9}:!1,animate:u?{opacity:1,y:0,scale:1}:void 0,whileTap:u?{scale:.97}:void 0,transition:{delay:.05+h*.035,duration:.45,ease:[.16,1,.3,1]},onClick:o?()=>o({...n,id:e.id}):void 0,className:`group flex w-[6.75rem] flex-col items-center rounded-xl ${M} pb-3 pt-4 text-center transition-[background-color,transform] duration-200 ease-out hover:bg-oai-gray-50 active:scale-[0.97] dark:hover:bg-oai-gray-900/70 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-oai-brand/60 sm:w-[7.75rem]`,"aria-label":l("achievements.badge.aria",{name:f,tier:d?l(`achievements.tier.${d}`):l("achievements.modal.locked")}),children:[s.jsx(B,{badgeId:e.id,tier:n.tier,locked:!a,size:m,className:"transition-transform duration-200 ease-out group-hover:-translate-y-0.5 group-hover:scale-105"}),s.jsxs("span",{className:"mt-2.5 flex w-full items-center justify-center gap-1",children:[s.jsx("span",{title:f,className:"min-w-0 truncate text-xs font-medium leading-tight text-oai-gray-800 dark:text-oai-gray-200",children:f}),e.scope==="local"&&s.jsx("span",{title:l("achievements.page.local_note"),"aria-label":l("achievements.scope.local"),className:"inline-flex shrink-0 text-oai-gray-400 dark:text-oai-gray-500",children:s.jsx(F,{size:11,strokeWidth:2,"aria-hidden":!0})})]}),s.jsx("span",{className:"mt-1 flex h-4 w-full items-center justify-center",children:a?s.jsx("span",{className:"text-[10px] font-semibold uppercase tracking-[0.08em]",style:{color:E[d]?.label},children:l(`achievements.tier.${d}`)}):i?s.jsx("span",{className:"h-[3px] w-14 overflow-hidden rounded-full bg-oai-gray-200 dark:bg-oai-gray-800",children:s.jsx("span",{className:"block h-full rounded-full bg-oai-brand/70",style:{width:`${Math.round(A*100)}%`}})}):null})]},e.id)})})}export{K as A,H as b,V as f,q as l};
1
+ import{m as _,j as s,c as l}from"./__vite-browser-external-BDkeh90N.js";import{m as $}from"./react-BmFZKA_b.js";import{B as x,s as b,b as C,t as T,A as B,a as E}from"./AchievementBadge-BX4iBBYM.js";import{f as L}from"./format-tokens-C4G-_xYt.js";import{u as I}from"./use-reduced-motion-BqDKim_3.js";const D=[["path",{d:"M10 16h.01",key:"1bzywj"}],["path",{d:"M2.212 11.577a2 2 0 0 0-.212.896V18a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-5.527a2 2 0 0 0-.212-.896L18.55 5.11A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z",key:"18tbho"}],["path",{d:"M21.946 12.013H2.054",key:"zqlbp7"}],["path",{d:"M6 16h.01",key:"1pmjb7"}]],F=_("hard-drive",D);function V(r,i){const t=Number(i)||0;switch(r){case"tokens":return L(t);case"rank":return`#${Math.round(t)}`;case"multiplier":return`${(Math.round(t*10)/10).toString()}×`;default:return Math.round(t).toLocaleString()}}function H(r){if(!r)return 0;const i=Number(r.next_threshold),t=Number(r.metric_value??r.value);if(!Number.isFinite(i)||i<=0)return r.tier>=4?1:0;if(!Number.isFinite(t)||t<=0)return 0;const o=r.lower_is_better?i/t:t/i;return Math.max(0,Math.min(1,o))}function q(r){const i=r?.achieved||{},t=Object.values(i).map(o=>o?Date.parse(o):NaN).filter(o=>Number.isFinite(o));return t.length>0?Math.max(...t):null}function K({achievements:r,isOwn:i=!1,scope:t,onSelect:o,size:m="xl",columns:v="default",animateIn:y=!1,className:N=""}){const k=I(),u=y&&!k,p=new Map;for(const e of Array.isArray(r)?r:[])e&&e.id&&p.set(e.id,e);const g=t?x.filter(e=>e.scope===t):x;let c;if(i){const e=g.map(a=>({entry:a,record:p.get(a.id)||{id:a.id,tier:0}})),n=b(e.filter(a=>(a.record.tier||0)>=1).map(a=>({...a,id:a.entry.id,tier:a.record.tier}))),h=e.filter(a=>(a.record.tier||0)<1);c=[...n,...h]}else c=b(g.map(e=>({entry:e,record:p.get(e.id)})).filter(e=>e.record&&(e.record.tier||0)>=1).map(e=>({...e,id:e.entry.id,tier:e.record.tier}))).map(({entry:e,record:n})=>({entry:e,record:n}));if(c.length===0)return null;const j=v==="wide"?"grid-cols-[repeat(3,max-content)] sm:grid-cols-[repeat(4,max-content)] lg:grid-cols-[repeat(5,max-content)]":"grid-cols-[repeat(3,max-content)] sm:grid-cols-[repeat(4,max-content)]",w=m==="lg"?"mx-0 sm:-mx-2":"-mx-2 sm:-mx-4",M=m==="lg"?"px-0":"px-1";return s.jsx("div",{className:`${w} grid justify-between gap-y-7 ${j} ${N}`,children:c.map(({entry:e,record:n},h)=>{const a=(n.tier||0)>=1,f=l(C(e.id,"name")),d=a?T(n.tier):null,A=H(n);return s.jsxs($.button,{type:"button",initial:u?{opacity:0,y:16,scale:.9}:!1,animate:u?{opacity:1,y:0,scale:1}:void 0,whileTap:u?{scale:.97}:void 0,transition:{delay:.05+h*.035,duration:.45,ease:[.16,1,.3,1]},onClick:o?()=>o({...n,id:e.id}):void 0,className:`group flex w-[6.75rem] flex-col items-center rounded-xl ${M} pb-3 pt-4 text-center transition-[background-color,transform] duration-200 ease-out hover:bg-oai-gray-50 active:scale-[0.97] dark:hover:bg-oai-gray-900/70 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-oai-brand/60 sm:w-[7.75rem]`,"aria-label":l("achievements.badge.aria",{name:f,tier:d?l(`achievements.tier.${d}`):l("achievements.modal.locked")}),children:[s.jsx(B,{badgeId:e.id,tier:n.tier,locked:!a,size:m,className:"transition-transform duration-200 ease-out group-hover:-translate-y-0.5 group-hover:scale-105"}),s.jsxs("span",{className:"mt-2.5 flex w-full items-center justify-center gap-1",children:[s.jsx("span",{title:f,className:"min-w-0 truncate text-xs font-medium leading-tight text-oai-gray-800 dark:text-oai-gray-200",children:f}),e.scope==="local"&&s.jsx("span",{title:l("achievements.page.local_note"),"aria-label":l("achievements.scope.local"),className:"inline-flex shrink-0 text-oai-gray-400 dark:text-oai-gray-500",children:s.jsx(F,{size:11,strokeWidth:2,"aria-hidden":!0})})]}),s.jsx("span",{className:"mt-1 flex h-4 w-full items-center justify-center",children:a?s.jsx("span",{className:"text-[10px] font-semibold uppercase tracking-[0.08em]",style:{color:E[d]?.label},children:l(`achievements.tier.${d}`)}):i?s.jsx("span",{className:"h-[3px] w-14 overflow-hidden rounded-full bg-oai-gray-200 dark:bg-oai-gray-800",children:s.jsx("span",{className:"block h-full rounded-full bg-oai-brand/70",style:{width:`${Math.round(A*100)}%`}})}):null})]},e.id)})})}export{K as A,H as b,V as f,q as l};
@@ -1 +1 @@
1
- import{r as L,j as a,b5 as De,c as h,e as ye,X as Ie}from"./__vite-browser-external-BGnq8Ebm.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-yHv9MTUA.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-DKyElm0T.js";import{u as Qe,a as z}from"./use-transform-Cy1ykjEs.js";import{u as et}from"./use-reduced-motion-DGXcZBX3.js";import{l as ce,f as ue,b as tt}from"./AchievementsSection-DN69LzXX.js";import{D as nt,a as rt,b as at,c as st,d as it,e as ot}from"./DialogTitle-_ahf0z8b.js";import"./zap-DStzvQJI.js";import"./flame-B_X-7VNp.js";import"./format-tokens-DEx2TJj8.js";import"./composite-Dxmivlk4.js";import"./store-oQZR4d9M.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 Ut({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{Ut as BadgeDetailModal,Tt as currentBadgeIssueNumber,Ut as default,Vt as formatIssueNumber};
1
+ import{r as L,j as a,b4 as De,c as h,e as ye,X as Ie}from"./__vite-browser-external-BDkeh90N.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-BmFZKA_b.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-BX4iBBYM.js";import{u as Qe,a as z}from"./use-transform-D42P7jkd.js";import{u as et}from"./use-reduced-motion-BqDKim_3.js";import{l as ce,f as ue,b as tt}from"./AchievementsSection-BM_JWWhJ.js";import{D as nt,a as rt,b as at,c as st,d as it,e as ot}from"./DialogTitle-DL5nvD31.js";import"./zap-CEq2616W.js";import"./flame-BamzyXzF.js";import"./format-tokens-C4G-_xYt.js";import"./composite-BIgEqWZD.js";import"./store-JM-a8ElZ.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 Ut({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{Ut as BadgeDetailModal,Tt as currentBadgeIssueNumber,Ut as default,Vt as formatIssueNumber};