tokentracker-cli 0.99.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.de.md +6 -5
- package/README.ja.md +6 -5
- package/README.ko.md +6 -5
- package/README.md +7 -5
- package/README.zh-CN.md +6 -5
- package/dashboard/dist/assets/{AchievementBadge-kRsjlB3p.js → AchievementBadge-pib0F8p8.js} +1 -1
- package/dashboard/dist/assets/{AchievementsPage-C3xybY0k.js → AchievementsPage-BMnub6CL.js} +2 -2
- package/dashboard/dist/assets/{AchievementsSection-y5mok2w0.js → AchievementsSection-DG_-yxAt.js} +1 -1
- package/dashboard/dist/assets/{BadgeDetailModal-IFLXYuKp.js → BadgeDetailModal-Sk2dHIkJ.js} +1 -1
- package/dashboard/dist/assets/{BotAnimated-D6x0PDt2.js → BotAnimated-VwwvOniA.js} +1 -1
- package/dashboard/dist/assets/{Card-CiKwEyVu.js → Card-COfjRX96.js} +1 -1
- package/dashboard/dist/assets/{ClawdAnimated-BQFcAIOy.js → ClawdAnimated-C3sF5c29.js} +2 -2
- package/dashboard/dist/assets/{CommandPalette-BvpX8PM_.js → CommandPalette-DvJFPsR2.js} +1 -1
- package/dashboard/dist/assets/{CommunityStatsModal-D5rNlwfO.js → CommunityStatsModal-DfYHIeKr.js} +1 -1
- package/dashboard/dist/assets/{ConfirmModal-CR0Evu8-.js → ConfirmModal-DtoHm0RD.js} +1 -1
- package/dashboard/dist/assets/{Controls-lyLUkbJW.js → Controls-DHHn10dy.js} +1 -1
- package/dashboard/dist/assets/{DashboardPage-X8CLr_au.js → DashboardPage-DJ_nDPjA.js} +1 -1
- package/dashboard/dist/assets/DevicePage-DlXWUpau.js +1 -0
- package/dashboard/dist/assets/{DialogClose-Bplj_718.js → DialogClose-BLegTe7F.js} +1 -1
- package/dashboard/dist/assets/DialogDescription-Dl_wqXjc.js +1 -0
- package/dashboard/dist/assets/{DialogTitle-Ckzf1tdi.js → DialogTitle-BZb-SEg0.js} +1 -1
- package/dashboard/dist/assets/{FadeIn-C2Jqe95f.js → FadeIn-Dsk0V4DK.js} +1 -1
- package/dashboard/dist/assets/{HeaderGithubStar-DgE7Cd5u.js → HeaderGithubStar-B20nLMWQ.js} +1 -1
- package/dashboard/dist/assets/{HoverTooltip-CEmo0N-P.js → HoverTooltip-tGFmzZiJ.js} +1 -1
- package/dashboard/dist/assets/{IpCheckPage-BwkpsLaF.js → IpCheckPage-BK7XI_aT.js} +1 -1
- package/dashboard/dist/assets/{LandingPage-CA2klwbE.js → LandingPage-BYX6NXbl.js} +2 -2
- package/dashboard/dist/assets/{LeaderboardAvatar-B_PUenGZ.js → LeaderboardAvatar-CIDqKSNn.js} +1 -1
- package/dashboard/dist/assets/{LeaderboardPage-D6HGW3jX.js → LeaderboardPage-BxIpRUwn.js} +2 -2
- package/dashboard/dist/assets/{LeaderboardProfileModal-Bfo1dSsS.js → LeaderboardProfileModal-DJlNN9YG.js} +3 -3
- package/dashboard/dist/assets/{LeaderboardProfilePage-2OqwrqOs.js → LeaderboardProfilePage-D4HSgrjG.js} +1 -1
- package/dashboard/dist/assets/{LimitsPage-BHrkLXHZ.js → LimitsPage-BKTMBbKS.js} +1 -1
- package/dashboard/dist/assets/{LocalOnlyNotice-C45MmWYt.js → LocalOnlyNotice-DpZiPX4A.js} +1 -1
- package/dashboard/dist/assets/{LoginCard-BikBibax.js → LoginCard-Cd03cg54.js} +1 -1
- package/dashboard/dist/assets/{LoginModal-D0Aq85Zg.js → LoginModal-yPZi5JsE.js} +1 -1
- package/dashboard/dist/assets/{LoginPage-B-kQaDQ_.js → LoginPage-BTL9oeVV.js} +1 -1
- package/dashboard/dist/assets/{PetPage-ZgPREiwD.js → PetPage-BWMSKd_3.js} +1 -1
- package/dashboard/dist/assets/{PopoverPopup-eQgg3h1i.js → PopoverPopup-Dba9PYte.js} +1 -1
- package/dashboard/dist/assets/{ProviderIcon-nDBPxn0N.js → ProviderIcon-DXI7Ztov.js} +1 -1
- package/dashboard/dist/assets/{ResetPasswordPage-CjsOtiGr.js → ResetPasswordPage-wKDU07tl.js} +1 -1
- package/dashboard/dist/assets/{SegmentedControl-6NcttkEH.js → SegmentedControl-DZh5rJHn.js} +1 -1
- package/dashboard/dist/assets/{Select-CzEhPICe.js → Select-CiJIsDf1.js} +1 -1
- package/dashboard/dist/assets/{SelectItemText-BwvzfXwj.js → SelectItemText-B8YkLJtE.js} +1 -1
- package/dashboard/dist/assets/{ServiceStatusPage-C35wpxrT.js → ServiceStatusPage-CF_bMZMz.js} +1 -1
- package/dashboard/dist/assets/{SessionsPage-a30adkhf.js → SessionsPage-B0PnK1TY.js} +1 -1
- package/dashboard/dist/assets/{SettingsPage-C_7ZPBUF.js → SettingsPage-onYZIk-h.js} +2 -2
- package/dashboard/dist/assets/{SkillsPage-DsxkFnrV.js → SkillsPage-CSyoblke.js} +1 -1
- package/dashboard/dist/assets/{TokenGalaxy-D_bXRotA.js → TokenGalaxy-kK6wMGuy.js} +1 -1
- package/dashboard/dist/assets/{ToolbarRootContext-CL7ma7ba.js → ToolbarRootContext-CnffyHPW.js} +1 -1
- package/dashboard/dist/assets/{TrendMonitor-QZBF2IEY.js → TrendMonitor-CpxDm5q_.js} +1 -1
- package/dashboard/dist/assets/{WidgetsPage-CWlxR0H-.js → WidgetsPage-vBedlR_d.js} +1 -1
- package/dashboard/dist/assets/{WrappedPage-D8XQ72nF.js → WrappedPage-D25YzXGY.js} +1 -1
- package/dashboard/dist/assets/{__vite-browser-external-CTGqjrCe.js → __vite-browser-external-OxXsgcds.js} +3 -3
- package/dashboard/dist/assets/{agent-logos-BscA25fS.js → agent-logos-frktji23.js} +1 -1
- package/dashboard/dist/assets/{arrow-up-right-DTxCEGbR.js → arrow-up-right-BtBcf39e.js} +1 -1
- package/dashboard/dist/assets/{check-CCEoh2wj.js → check-PVwhLtMf.js} +1 -1
- package/dashboard/dist/assets/{composite-B2s880f5.js → composite-BxlyIOoF.js} +1 -1
- package/dashboard/dist/assets/{copy-CJVQglwG.js → copy-s5UqKoXC.js} +1 -1
- package/dashboard/dist/assets/{download-Cn-OpYm6.js → download-Ns8TNfey.js} +1 -1
- package/dashboard/dist/assets/{external-link-BnYlUn56.js → external-link-zpvfw1sA.js} +1 -1
- package/dashboard/dist/assets/{flame-D4yFLV1L.js → flame-DhPzeD5Z.js} +1 -1
- package/dashboard/dist/assets/{format-tokens-CBUNbBvp.js → format-tokens-C4eAHkPw.js} +1 -1
- package/dashboard/dist/assets/{icons-CcgNgAvb.js → icons-CNeKwVFm.js} +1 -1
- package/dashboard/dist/assets/{index-CTjeDkbu.js → index-9qLVgAvh.js} +1 -1
- package/dashboard/dist/assets/{index-CkELwUyg.js → index-B5UHZH4-.js} +1 -1
- package/dashboard/dist/assets/{index-ZWeEKbo-.js → index-D8ciCYQM.js} +1 -1
- package/dashboard/dist/assets/{info-DoehRSQU.js → info-p2pKHzPt.js} +1 -1
- package/dashboard/dist/assets/{link-2-D8xSsPqy.js → link-2-BQhZyxUQ.js} +1 -1
- package/dashboard/dist/assets/{loader-circle-DMu0-X5r.js → loader-circle-BnFE5J64.js} +1 -1
- package/dashboard/dist/assets/{maximize-2-DLyYsZvE.js → maximize-2-Zt1L2xuV.js} +1 -1
- package/dashboard/dist/assets/{modifiers.esm-Dm5Xy2jA.js → modifiers.esm-Sbhv1CrS.js} +1 -1
- package/dashboard/dist/assets/{monitor-smartphone-5uZGQNGJ.js → monitor-smartphone-Pmy9ktPf.js} +1 -1
- package/dashboard/dist/assets/{plus-8fnU0dkt.js → plus-FPP4P4tW.js} +1 -1
- package/dashboard/dist/assets/{provider-display-B3ed-pSc.js → provider-display-zpzUqFZ8.js} +2 -2
- package/dashboard/dist/assets/{react-Gft01KYk.js → react-C_5Bvle-.js} +1 -1
- package/dashboard/dist/assets/{search-jqzJg2kG.js → search-7f0zYUoC.js} +1 -1
- package/dashboard/dist/assets/{skills-api-Bwn_xcHV.js → skills-api-CdEcHyY0.js} +1 -1
- package/dashboard/dist/assets/{sortable.esm-CYjzLCp7.js → sortable.esm-DwOWVPK8.js} +1 -1
- package/dashboard/dist/assets/{store-CgJHe9jU.js → store-D3n2aJ7w.js} +1 -1
- package/dashboard/dist/assets/{terminal-CZ5j8yEQ.js → terminal-C6zDcArO.js} +1 -1
- package/dashboard/dist/assets/{trash-DtMLjI_S.js → trash-C_bcv-F0.js} +1 -1
- package/dashboard/dist/assets/{use-community-stats-Px3dHGlR.js → use-community-stats-fCkrn5Kd.js} +1 -1
- package/dashboard/dist/assets/{use-latest-request-guard-B7Vw4Wsd.js → use-latest-request-guard-DD2Nt1qS.js} +1 -1
- package/dashboard/dist/assets/{use-limits-display-prefs-DWWrcMPi.js → use-limits-display-prefs-a9HtQcOE.js} +1 -1
- package/dashboard/dist/assets/{use-ordered-list-CevbB79U.js → use-ordered-list-B6-Nb6rL.js} +1 -1
- package/dashboard/dist/assets/use-reduced-motion-DyXrwS-h.js +1 -0
- package/dashboard/dist/assets/{use-session-efficiency-pref-DoIaI0vm.js → use-session-efficiency-pref-gD9i2h4k.js} +1 -1
- package/dashboard/dist/assets/{use-transform-qRwUetOk.js → use-transform-DP6mCHUn.js} +1 -1
- package/dashboard/dist/assets/{use-usage-limits-DlP7yg78.js → use-usage-limits-TdwK-gf2.js} +1 -1
- package/dashboard/dist/assets/{useCurrency-DRUe8YoY.js → useCurrency-Bbxwhe-A.js} +1 -1
- package/dashboard/dist/assets/{useTokenFormat-CmLbznUC.js → useTokenFormat-CzeTrne_.js} +1 -1
- package/dashboard/dist/assets/{zap-CS80c_uN.js → zap-CucEQWyU.js} +1 -1
- package/dashboard/dist/index.html +13 -11
- package/dashboard/dist/ip-check.html +1 -1
- package/dashboard/dist/leaderboard.html +1 -1
- package/dashboard/dist/llms.txt +3 -2
- package/dashboard/dist/share.html +1 -1
- package/package.json +2 -2
- package/src/commands/init.js +10 -0
- package/src/commands/status.js +16 -0
- package/src/commands/sync.js +43 -1
- package/src/lib/codex-rollout-parser.js +86 -8
- package/src/lib/codex-usage-record.js +195 -0
- package/src/lib/pricing/seed-snapshot.json +1 -1
- package/src/lib/rollout.js +261 -11
- package/src/lib/skills-manager.js +2 -2
- package/dashboard/dist/assets/DevicePage-RCIANm45.js +0 -1
- package/dashboard/dist/assets/DialogDescription-C2EzE7DU.js +0 -1
- package/dashboard/dist/assets/use-reduced-motion-CCuEY-79.js +0 -1
package/README.de.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
### Sieh genau, was du für KI ausgibst – über jedes CLI hinweg
|
|
8
8
|
|
|
9
|
-
Sammle automatisch Token-Zahlen von **
|
|
9
|
+
Sammle automatisch Token-Zahlen von **40 KI-Coding-Tools**, aggregiere sie lokal und sieh echte Kostentrends in einem schönen Dashboard. Kein Cloud-Konto, keine API-Keys, kein Setup – nur ein Befehl.
|
|
10
10
|
|
|
11
11
|
[](https://www.npmjs.com/package/tokentracker-cli)
|
|
12
12
|
[](https://www.npmjs.com/package/tokentracker-cli)
|
|
@@ -88,7 +88,7 @@ Aktualisieren mit `brew upgrade --cask xiufengsun/tokentracker/tokentracker`. De
|
|
|
88
88
|
|
|
89
89
|
## ✨ Features
|
|
90
90
|
|
|
91
|
-
- 🔌 **
|
|
91
|
+
- 🔌 **40 KI-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, OmO, 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, MiniMax Code
|
|
92
92
|
- 🏠 **Local-First** — Läuft auf deinem Rechner. Parst Logs lokal, ohne Konto oder API-Keys.
|
|
93
93
|
- 🚀 **Zero Config** — Hooks installieren sich beim ersten Start automatisch. Von null zum Dashboard in 30 Sekunden.
|
|
94
94
|
- 📊 **Schönes Dashboard** — Nutzungstrends, Kostenaufschlüsselung nach Modell, GitHub-ähnliche Aktivitäts-Heatmap, Projektzuordnung
|
|
@@ -200,6 +200,7 @@ Aktualisieren mit `brew upgrade --cask xiufengsun/tokentracker/tokentracker`. De
|
|
|
200
200
|
| **Unsloth Studio** | ✅ Auto | Passiver SQLite-Reader für `$UNSLOTH_STUDIO_HOME/studio.db` (Standard `~/.unsloth/studio/studio.db`). Liest nur skalare `contextUsage`-Metadaten und inhaltsfreie `api_usage_events`; Prompts, Antworten, Anhänge, API-Subjects, Zugangsdaten und Trainingsmetriken bleiben ausgeschlossen. Lokale Routen bleiben bei $0, bekannte kostenpflichtige Provider werden anhand des tatsächlich antwortenden Modells geschätzt. |
|
|
201
201
|
| **AnythingLLM Desktop** | ✅ Auto | Passiver SQLite-Reader (`anythingllm-desktop/storage/anythingllm.db`, nur Token-Metriken pro Nachricht) |
|
|
202
202
|
| **Devin CLI** (Cognition) | ✅ Auto | Passiver SQLite-Reader (`$XDG_DATA_HOME/devin/cli/sessions.db`, Standard `~/.local/share/devin/cli/sessions.db`). Liest Usage-Metriken pro Anfrage, dedupliziert nach `request_id` — Replay-/Fork-/Compaction-Kopien werden nicht doppelt gezählt — und verwendet das aufgezeichnete Generation-Modell; niemals Prompts, Antworten oder `cogs_json`. Für Devin-Modelle (`swe-2`, `swe-2-high`, `compactor`) gibt es derzeit keine Preisdaten: Token-Zahlen werden erfasst, aber nicht in Dollar-Schätzungen einbezogen — ein $0-Wert bedeutet keine kostenlose Nutzung. Kein bekanntes natives Windows-Datenverzeichnis; eine WSL-Installation wird über `\\wsl$` gelesen. |
|
|
203
|
+
| **MiniMax Code** | ✅ Auto | Passiver Reader (`~/.minimax/v2/sessions/YYYY/MM/DD/<session>/messages.jsonl`). Dedupliziert nach `message_id`, erfasst das geroutete Upstream-Modell pro Nachricht und ignoriert das stets leere `usage.cost` zugunsten der normalen Preise. Verzeichnis per `TOKENTRACKER_MINIMAX_HOME` überschreibbar |
|
|
203
204
|
| **Claude Science** | ✅ Auto | Passiver SQLite-Reader (`~/.claude-science/operon-cli.db`, nur die Token-Zähler der `frames`-Tabelle; keine Prompts, Artefakte oder Forschungsinhalte). Kein natives Windows-Build — unter Windows läuft die App in WSL und wird von dort gelesen. |
|
|
204
205
|
| **DeepSeek Harness** | ✅ Auto | Passiver Session-Reader (`~/.dsh/sessions/**/session.jsonl[.zstd]`; liest Session-Header und Assistant-Ereignisse, unterstützt Zstandard mit mehreren Frames) |
|
|
205
206
|
| **TRAE Work CN** | ✅ Auto | **Erfordert eine ausdrückliche Zustimmung: `TOKENTRACKER_TRAE_CN_USAGE=1` setzen.** Das Lesen der Nutzung überträgt die lokal gespeicherte Anmeldeautorisierung an die interne API von TRAE, daher wird nichts gesendet, bevor du es aktivierst. Danach: liest bei vorhandener lokaler TRAE Work CN-Anmeldung während eines zulässigen Syncs außerhalb des Hintergrundbetriebs unter macOS die Session-Token-Nutzung der angemeldeten App; die interne API kann sich ändern |
|
|
@@ -207,7 +208,7 @@ Aktualisieren mit `brew upgrade --cask xiufengsun/tokentracker/tokentracker`. De
|
|
|
207
208
|
> **Muss ich Plugins oder Hooks manuell installieren?** Nein. `tokentracker` (oder `tokentracker init`) erledigt alles beim ersten Start:
|
|
208
209
|
> - **Hook-basiert** (Claude Code, Codex, AStudio, Gemini, Every Code, CodeBuddy, WorkBuddy, Grok Build) — wir schreiben einen SessionEnd-Hook oder TOML-Notify-Eintrag in die Konfiguration des Tools.
|
|
209
210
|
> - **Plugin-basiert** (OpenCode, OpenClaw) — das Plugin ist im npm-Paket enthalten (`~/.tokentracker/app/openclaw-plugin/`). Wir verlinken es per CLI (`openclaw plugins install --link …` + `enable`). Kein Download, kein Drag-and-Drop.
|
|
210
|
-
> - **Passive Reader** (Cursor, Kiro, Hermes, Kimi Code, Copilot, Grok Build, oh-my-pi, pi, Craft Agents, Reasonix, Kilo CLI, Kilo Code, Roo Code, Antigravity, Zed Agent, Goose, Droid, Mimo Code, ZCode, LM Studio, Unsloth Studio, AnythingLLM Desktop, Devin CLI, Claude Science, DeepSeek Harness) — wir installieren nichts in diesen Tools. Wir lesen nur Dateien, die sie bereits produzieren (SQLite-DB, JSONL, OTEL-Export, Session-Logs). Die Nutzung von Copilot App / CLI wird pro Anfrage aus `~/.copilot/session-store.db` gelesen; `data.db` liefert einmalig die Legacy-Migrationsbasis und bleibt nach der kanonischen Übernahme des Stores schreibgeschützt im Beobachtungsmodus, während Chat-Erweiterung und ältere CLI-Versionen weiterhin OTEL verwenden. TokenTracker koordiniert diese Quellen, damit überlappende Anfragen nur einmal gezählt werden. Gemischte App/CLI-Historie vor der Übernahme bleibt als `github-copilot-legacy`-Aggregat erhalten, statt einem geratenen Anfrage-Modell zugeordnet zu werden.
|
|
211
|
+
> - **Passive Reader** (Cursor, Kiro, Hermes, Kimi Code, Copilot, Grok Build, oh-my-pi, pi, Craft Agents, Reasonix, Kilo CLI, Kilo Code, Roo Code, Antigravity, Zed Agent, Goose, Droid, Mimo Code, ZCode, LM Studio, Unsloth Studio, AnythingLLM Desktop, Devin CLI, MiniMax Code, Claude Science, DeepSeek Harness) — wir installieren nichts in diesen Tools. Wir lesen nur Dateien, die sie bereits produzieren (SQLite-DB, JSONL, OTEL-Export, Session-Logs). Die Nutzung von Copilot App / CLI wird pro Anfrage aus `~/.copilot/session-store.db` gelesen; `data.db` liefert einmalig die Legacy-Migrationsbasis und bleibt nach der kanonischen Übernahme des Stores schreibgeschützt im Beobachtungsmodus, während Chat-Erweiterung und ältere CLI-Versionen weiterhin OTEL verwenden. TokenTracker koordiniert diese Quellen, damit überlappende Anfragen nur einmal gezählt werden. Gemischte App/CLI-Historie vor der Übernahme bleibt als `github-copilot-legacy`-Aggregat erhalten, statt einem geratenen Anfrage-Modell zugeordnet zu werden.
|
|
211
212
|
>
|
|
212
213
|
> Führe `tokentracker status` aus, um den Status jeder Integration zu prüfen. Zeigt ein Tool `skipped`, erklärt die `detail`-Spalte warum.
|
|
213
214
|
>
|
|
@@ -223,7 +224,7 @@ Fehlt dein Tool? [Erstelle ein Issue](https://github.com/xiufengsun/TokenTracker
|
|
|
223
224
|
|
|
224
225
|
| Funktion | **[TokenTracker](https://github.com/xiufengsun/TokenTracker)** | **[ccusage](https://github.com/ccusage/ccusage)** | **[Tokscale](https://github.com/junhoyeo/tokscale)** |
|
|
225
226
|
|---|:---:|:---:|:---:|
|
|
226
|
-
| **Unterstützte KI-Tools** | **
|
|
227
|
+
| **Unterstützte KI-Tools** | **40** | Multi-Agent | Multi-Agent |
|
|
227
228
|
| **Primäre Benutzeroberfläche** | Native Desktop-Apps & Web-Dashboard | Terminal-CLI | Terminal-TUI & CLI |
|
|
228
229
|
| **Local-First-Analyse** | ✅ | ✅ | ✅ |
|
|
229
230
|
| **Native Desktop-Apps** | ✅ macOS, Windows, Linux | ❌ | ❌ |
|
|
@@ -239,7 +240,7 @@ Fehlt dein Tool? [Erstelle ein Issue](https://github.com/xiufengsun/TokenTracker
|
|
|
239
240
|
|
|
240
241
|
```mermaid
|
|
241
242
|
flowchart LR
|
|
242
|
-
A["KI-Coding-Tools<br/>Claude · 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"]
|
|
243
|
+
A["KI-Coding-Tools<br/>Claude · 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 · MiniMax Code"]
|
|
243
244
|
A -->|Hooks lösen aus| B[Token Tracker]
|
|
244
245
|
B -->|Logs parsen<br/>30-Min-UTC-Buckets| C[(Lokales SQLite)]
|
|
245
246
|
C --> D[Web-Dashboard]
|
package/README.ja.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
### AI に使ったコストを正確に把握 — すべての CLI を横断して
|
|
8
8
|
|
|
9
|
-
**
|
|
9
|
+
**40 種類の AI コーディングツール**からトークン数を自動収集し、ローカルで集計、美しいダッシュボードで本当のコスト推移を可視化します。クラウドアカウント不要、API キー不要、セットアップ不要 — コマンド 1 つで完了です。
|
|
10
10
|
|
|
11
11
|
[](https://www.npmjs.com/package/tokentracker-cli)
|
|
12
12
|
[](https://www.npmjs.com/package/tokentracker-cli)
|
|
@@ -88,7 +88,7 @@ brew install xiufengsun/tokentracker/tokentracker
|
|
|
88
88
|
|
|
89
89
|
## ✨ 機能
|
|
90
90
|
|
|
91
|
-
- 🔌 **
|
|
91
|
+
- 🔌 **40 種類の AI ツールを標準対応** — 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、OmO、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、MiniMax Code
|
|
92
92
|
- 🏠 **ローカルファースト** — すべてマシン上で動作。アカウントや API キーなしでログをローカル解析します。
|
|
93
93
|
- 🚀 **ゼロコンフィグ** — Hook は初回実行で自動インストール。0 からダッシュボードまで 30 秒。
|
|
94
94
|
- 📊 **美しいダッシュボード** — 使用トレンド、モデル別コスト内訳、GitHub スタイルのアクティビティヒートマップ、プロジェクト別の帰属表示
|
|
@@ -201,6 +201,7 @@ brew install xiufengsun/tokentracker/tokentracker
|
|
|
201
201
|
| **Unsloth Studio** | ✅ 自動 | `$UNSLOTH_STUDIO_HOME/studio.db`(既定 `~/.unsloth/studio/studio.db`)のパッシブ SQLite リーダー。スカラー `contextUsage` と内容を含まない `api_usage_events` のみを読み、prompt、reply、添付、API subject、認証情報、学習指標は除外します。ローカル経路は $0、既知の有料 provider は実際に応答したモデルで token コストを推定します。 |
|
|
202
202
|
| **AnythingLLM Desktop** | ✅ 自動 | パッシブな SQLite リーダー (`anythingllm-desktop/storage/anythingllm.db`、メッセージごとの token 指標のみ) |
|
|
203
203
|
| **Devin CLI** (Cognition) | ✅ 自動 | パッシブな SQLite リーダー (`$XDG_DATA_HOME/devin/cli/sessions.db`、デフォルト `~/.local/share/devin/cli/sessions.db`)。`request_id` でリクエスト単位の usage 指標を重複排除 (replay/fork/compaction のコピーは二重計上しない) し、記録された generation model を使います。prompt・応答・`cogs_json` は読みません。Devin のモデル (`swe-2`、`swe-2-high`、`compactor`) には現在価格データがないため、token 数は報告されますがドル見積りからは除外されます — $0 表示は無料を意味しません。ネイティブ Windows のデータディレクトリは未確認で、WSL インストールは `\\wsl$` 経由で読み取ります。 |
|
|
204
|
+
| **MiniMax Code** | ✅ 自動 | パッシブリーダー (`~/.minimax/v2/sessions/YYYY/MM/DD/<session>/messages.jsonl`)。`message_id` で重複排除し、メッセージごとにルーティング先の上流モデルを記録します。常に 0 の `usage.cost` は使わず通常の料金表で計算します。ディレクトリは `TOKENTRACKER_MINIMAX_HOME` で上書き可能 |
|
|
204
205
|
| **Claude Science** | ✅ 自動 | パッシブな SQLite リーダー (`~/.claude-science/operon-cli.db`、`frames` テーブルの token カウンタのみ。prompt・成果物・研究内容は読みません)。ネイティブ Windows 版はなく、Windows では WSL 内で動作するアプリを読み取ります。 |
|
|
205
206
|
| **DeepSeek Harness** | ✅ 自動 | パッシブなセッションリーダー (`~/.dsh/sessions/**/session.jsonl[.zstd]`、セッションヘッダーと assistant イベントを解析し、複数フレームの zstd 展開に対応) |
|
|
206
207
|
| **TRAE Work CN** | ✅ 自動 | **明示的なオプトインが必要です: `TOKENTRACKER_TRAE_CN_USAGE=1` を設定してください。** 使用量の読み取りはローカルに保存されたサインイン認証を TRAE の内部 API に送信するため、有効にするまで何も送信されません。有効化後: ローカル TRAE Work CN のサインイン認証がある場合、実行可能な非バックグラウンド同期中に macOS のサインイン済みアプリから session-token 使用量を読み取ります。内部 API は変更される可能性があります |
|
|
@@ -208,7 +209,7 @@ brew install xiufengsun/tokentracker/tokentracker
|
|
|
208
209
|
> **プラグインや hook を手動でインストールする必要はありますか?** いいえ。`tokentracker`(または `tokentracker init`)が初回実行ですべて処理します:
|
|
209
210
|
> - **Hook ベース**のツール (Claude Code、Codex、AStudio、Gemini、Every Code、**CodeBuddy**、**WorkBuddy**、**Grok Build**) — ツール自身の設定に SessionEnd hook または TOML notify エントリーを書き込みます。
|
|
210
211
|
> - **プラグインベース**のツール (OpenCode、**OpenClaw**) — プラグインは npm パッケージ内に同梱されています。OpenClaw のセッションプラグインは `~/.tokentracker/tracker/openclaw-plugin/openclaw-session-sync/` にあり、OpenClaw 自身の CLI でリンクして有効化したうえで、同期を起動するセッション終了イベントを許可するために `hooks.allowConversationAccess=true` を設定します。ダウンロードもドラッグ&ドロップも不要です。
|
|
211
|
-
> - **パッシブリーダー** (Cursor、Kiro、Hermes、Kimi Code、Copilot、**Grok Build**、**oh-my-pi**、**pi**、**Craft Agents**、**Reasonix**、**Kilo CLI**、**Kilo Code**、**Roo Code**、**Antigravity**、**Zed Agent**、**Goose**、**Droid**、**Mimo Code**、**ZCode**、**LM Studio**、**Unsloth Studio**、**AnythingLLM Desktop**、**Devin CLI**、**Claude Science**、**DeepSeek Harness**) — これらのツールには何もインストールしません。ツールがすでに出力しているファイル (SQLite DB、JSONL、OTEL エクスポート、session logs) を読むだけです。Copilot App / CLI の使用量は `~/.copilot/session-store.db` からリクエスト単位で読み取ります。`data.db` は旧データ移行時のベースラインとして一度だけ使い、store が正規ソースになった後は監視専用です。Chat 拡張と旧 CLI は引き続き OTEL を使用し、重複するリクエストは TokenTracker が一度だけ集計します。移行前の混在 App/CLI 履歴でモデルを安全に分離できない残量は、推測したリクエストモデルではなく `github-copilot-legacy` の集計値として保持します。
|
|
212
|
+
> - **パッシブリーダー** (Cursor、Kiro、Hermes、Kimi Code、Copilot、**Grok Build**、**oh-my-pi**、**pi**、**Craft Agents**、**Reasonix**、**Kilo CLI**、**Kilo Code**、**Roo Code**、**Antigravity**、**Zed Agent**、**Goose**、**Droid**、**Mimo Code**、**ZCode**、**LM Studio**、**Unsloth Studio**、**AnythingLLM Desktop**、**Devin CLI**、**MiniMax Code**、**Claude Science**、**DeepSeek Harness**) — これらのツールには何もインストールしません。ツールがすでに出力しているファイル (SQLite DB、JSONL、OTEL エクスポート、session logs) を読むだけです。Copilot App / CLI の使用量は `~/.copilot/session-store.db` からリクエスト単位で読み取ります。`data.db` は旧データ移行時のベースラインとして一度だけ使い、store が正規ソースになった後は監視専用です。Chat 拡張と旧 CLI は引き続き OTEL を使用し、重複するリクエストは TokenTracker が一度だけ集計します。移行前の混在 App/CLI 履歴でモデルを安全に分離できない残量は、推測したリクエストモデルではなく `github-copilot-legacy` の集計値として保持します。
|
|
212
213
|
> - **Grok Build の推定** — 現在のローカルテレメトリは `updates.jsonl` の累積 `totalTokens` を公開していますが、安定したプロンプト/出力/キャッシュの内訳はありません。`signals.json` は `contextTokensUsed` のスナップショットを使ったフォールバックとして残っています。コールごとの利用詳細が利用可能になるまで、TokenTracker は Grok のコストを推定します。
|
|
213
214
|
>
|
|
214
215
|
> いつでも `tokentracker status` を実行すれば、各統合の状態を確認できます。`skipped` と表示されている場合、`detail` 列にその理由が示されます(例: ツール CLI が `PATH` にない、設定が読めない)。
|
|
@@ -225,7 +226,7 @@ brew install xiufengsun/tokentracker/tokentracker
|
|
|
225
226
|
|
|
226
227
|
| 機能 | **[TokenTracker](https://github.com/xiufengsun/TokenTracker)** | **[ccusage](https://github.com/ccusage/ccusage)** | **[Tokscale](https://github.com/junhoyeo/tokscale)** |
|
|
227
228
|
|---|:---:|:---:|:---:|
|
|
228
|
-
| **対応 AI ツール数** | **
|
|
229
|
+
| **対応 AI ツール数** | **40** | マルチエージェント | マルチエージェント |
|
|
229
230
|
| **主なインターフェース** | ネイティブデスクトップアプリ & Web ダッシュボード | ターミナル CLI | ターミナル TUI & CLI |
|
|
230
231
|
| **ローカルファースト解析** | ✅ | ✅ | ✅ |
|
|
231
232
|
| **ネイティブデスクトップアプリ** | ✅ macOS, Windows, Linux | ❌ | ❌ |
|
|
@@ -241,7 +242,7 @@ brew install xiufengsun/tokentracker/tokentracker
|
|
|
241
242
|
|
|
242
243
|
```mermaid
|
|
243
244
|
flowchart LR
|
|
244
|
-
A["AI coding tools<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"]
|
|
245
|
+
A["AI coding tools<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 · MiniMax Code"]
|
|
245
246
|
A -->|hooks trigger| B[Token Tracker]
|
|
246
247
|
B -->|parse logs<br/>30-min UTC buckets| C[(Local SQLite)]
|
|
247
248
|
C --> D[Web Dashboard]
|
package/README.ko.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
### 모든 CLI에서 AI에 쓰는 비용을 정확히 파악
|
|
8
8
|
|
|
9
|
-
**
|
|
9
|
+
**40개의 AI 코딩 도구**에서 토큰 수치를 자동으로 수집하고 로컬에서 집계해, 실제 비용 추세를 아름다운 대시보드에서 확인. 클라우드 계정 불필요, API Key 불필요, 셋업 불필요 — 명령 한 줄이면 끝.
|
|
10
10
|
|
|
11
11
|
[](https://www.npmjs.com/package/tokentracker-cli)
|
|
12
12
|
[](https://www.npmjs.com/package/tokentracker-cli)
|
|
@@ -88,7 +88,7 @@ brew install xiufengsun/tokentracker/tokentracker
|
|
|
88
88
|
|
|
89
89
|
## ✨ 기능
|
|
90
90
|
|
|
91
|
-
- 🔌 **
|
|
91
|
+
- 🔌 **40개의 AI 도구 기본 지원** — 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, OmO, 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, MiniMax Code
|
|
92
92
|
- 🏠 **로컬 우선** — 내 컴퓨터에서 실행됩니다. 계정이나 API Key 없이 로그를 로컬에서 파싱합니다.
|
|
93
93
|
- 🚀 **제로 설정** — 첫 실행 시 Hook 자동 설치. 0에서 대시보드까지 30초.
|
|
94
94
|
- 📊 **아름다운 대시보드** — 사용 추세, 모델별 비용 분석, GitHub 스타일 활동 히트맵, 프로젝트 귀속 정보
|
|
@@ -201,6 +201,7 @@ brew install xiufengsun/tokentracker/tokentracker
|
|
|
201
201
|
| **Unsloth Studio** | ✅ 자동 | `$UNSLOTH_STUDIO_HOME/studio.db`(기본값 `~/.unsloth/studio/studio.db`) 패시브 SQLite 리더. 스칼라 `contextUsage`와 내용이 없는 `api_usage_events`만 읽으며 prompt, reply, 첨부, API subject, 자격 증명, 학습 지표는 제외합니다. 로컬 경로는 $0, 알려진 유료 provider 경로는 실제 응답 모델로 token 비용을 추정합니다. |
|
|
202
202
|
| **AnythingLLM Desktop** | ✅ 자동 | 패시브 SQLite 리더 (`anythingllm-desktop/storage/anythingllm.db`, 메시지별 token 지표만 읽음) |
|
|
203
203
|
| **Devin CLI** (Cognition) | ✅ 자동 | 패시브 SQLite 리더 (`$XDG_DATA_HOME/devin/cli/sessions.db`, 기본값 `~/.local/share/devin/cli/sessions.db`). `request_id`로 요청 단위 usage 지표를 중복 제거하고 (replay/fork/compaction 복사본은 이중 집계하지 않음) 기록된 generation model을 사용합니다. prompt·응답·`cogs_json`은 읽지 않습니다. Devin 모델 (`swe-2`, `swe-2-high`, `compactor`)에는 현재 가격 데이터가 없어 token 수는 보고되지만 달러 추정치에서 제외됩니다 — $0 표시가 무료를 의미하지는 않습니다. 네이티브 Windows 데이터 디렉터리는 확인되지 않았으며 WSL 설치는 `\\wsl$`을 통해 읽습니다. |
|
|
204
|
+
| **MiniMax Code** | ✅ 자동 | 패시브 리더 (`~/.minimax/v2/sessions/YYYY/MM/DD/<session>/messages.jsonl`). `message_id`로 중복 제거하고 메시지마다 라우팅된 상위 모델을 기록합니다. 항상 0인 `usage.cost`는 쓰지 않고 일반 가격표로 계산합니다. 디렉터리는 `TOKENTRACKER_MINIMAX_HOME`으로 변경 가능 |
|
|
204
205
|
| **Claude Science** | ✅ 자동 | 패시브 SQLite 리더 (`~/.claude-science/operon-cli.db`, `frames` 테이블의 token 카운터만 읽으며 prompt·산출물·연구 내용은 읽지 않음). 네이티브 Windows 빌드가 없어 Windows에서는 WSL 안에서 실행되는 앱을 읽습니다. |
|
|
205
206
|
| **DeepSeek Harness** | ✅ 자동 | 패시브 세션 리더 (`~/.dsh/sessions/**/session.jsonl[.zstd]`, 세션 헤더와 assistant 이벤트를 파싱하고 멀티 프레임 zstd 압축 해제를 지원) |
|
|
206
207
|
| **TRAE Work CN** | ✅ 자동 | **명시적인 옵트인이 필요합니다: `TOKENTRACKER_TRAE_CN_USAGE=1` 을 설정하세요.** 사용량을 읽으면 로컬에 저장된 로그인 인증이 TRAE의 내부 API로 전송되므로, 켜기 전에는 아무것도 전송되지 않습니다. 켠 뒤에는: 로컬 TRAE Work CN 인증이 있을 때 실행 가능한 비백그라운드 동기화 중에 macOS의 로그인된 앱에서 session-token 사용량을 읽습니다. 내부 API는 변경될 수 있습니다 |
|
|
@@ -208,7 +209,7 @@ brew install xiufengsun/tokentracker/tokentracker
|
|
|
208
209
|
> **플러그인이나 hook을 수동으로 설치해야 하나요?** 아니요. `tokentracker` (또는 `tokentracker init`)가 첫 실행에서 모든 것을 처리합니다:
|
|
209
210
|
> - **Hook 기반** 도구 (Claude Code, Codex, AStudio, Gemini, Every Code, **CodeBuddy**, **WorkBuddy**, **Grok Build**) — 도구 자체의 설정에 SessionEnd hook 또는 TOML notify 엔트리를 작성합니다.
|
|
210
211
|
> - **플러그인 기반** 도구 (OpenCode, **OpenClaw**) — 플러그인은 npm 패키지 안에 포함되어 있습니다. OpenClaw 세션 플러그인은 `~/.tokentracker/tracker/openclaw-plugin/openclaw-session-sync/`에 있으며, OpenClaw 자체 CLI로 링크하고 활성화한 뒤 동기화를 트리거하는 세션 종료 이벤트를 허용하도록 `hooks.allowConversationAccess=true`를 설정합니다. 다운로드, 드래그 앤 드롭 불필요.
|
|
211
|
-
> - **패시브 리더** (Cursor, Kiro, Hermes, Kimi Code, Copilot, **Grok Build**, **oh-my-pi**, **pi**, **Craft Agents**, **Reasonix**, **Kilo CLI**, **Kilo Code**, **Roo Code**, **Antigravity**, **Zed Agent**, **Goose**, **Droid**, **Mimo Code**, **ZCode**, **LM Studio**, **Unsloth Studio**, **AnythingLLM Desktop**, **Devin CLI**, **Claude Science**, **DeepSeek Harness**) — 이들 도구에는 아무것도 설치하지 않습니다. 도구가 이미 생성하는 파일 (SQLite DB, JSONL, OTEL export, session logs)만 읽습니다. Copilot App / CLI 사용량은 `~/.copilot/session-store.db`에서 요청별로 읽습니다. `data.db`는 레거시 마이그레이션 기준선으로 한 번만 사용하며 store가 정식 소스가 된 뒤에는 관찰 전용으로 유지됩니다. Chat 확장과 이전 CLI는 계속 OTEL을 사용하며, TokenTracker가 겹치는 요청을 한 번만 집계합니다. 마이그레이션 전 혼합 App/CLI 기록에서 모델을 안전하게 분리할 수 없는 잔여분은 추정 모델 대신 `github-copilot-legacy` 집계로 보존합니다.
|
|
212
|
+
> - **패시브 리더** (Cursor, Kiro, Hermes, Kimi Code, Copilot, **Grok Build**, **oh-my-pi**, **pi**, **Craft Agents**, **Reasonix**, **Kilo CLI**, **Kilo Code**, **Roo Code**, **Antigravity**, **Zed Agent**, **Goose**, **Droid**, **Mimo Code**, **ZCode**, **LM Studio**, **Unsloth Studio**, **AnythingLLM Desktop**, **Devin CLI**, **MiniMax Code**, **Claude Science**, **DeepSeek Harness**) — 이들 도구에는 아무것도 설치하지 않습니다. 도구가 이미 생성하는 파일 (SQLite DB, JSONL, OTEL export, session logs)만 읽습니다. Copilot App / CLI 사용량은 `~/.copilot/session-store.db`에서 요청별로 읽습니다. `data.db`는 레거시 마이그레이션 기준선으로 한 번만 사용하며 store가 정식 소스가 된 뒤에는 관찰 전용으로 유지됩니다. Chat 확장과 이전 CLI는 계속 OTEL을 사용하며, TokenTracker가 겹치는 요청을 한 번만 집계합니다. 마이그레이션 전 혼합 App/CLI 기록에서 모델을 안전하게 분리할 수 없는 잔여분은 추정 모델 대신 `github-copilot-legacy` 집계로 보존합니다.
|
|
212
213
|
> - **Grok Build 추정** — 현재 로컬 텔레메트리는 `updates.jsonl`의 누적 `totalTokens`를 노출하지만, 안정적인 프롬프트/출력/캐시 분할은 제공하지 않습니다; `signals.json`은 `contextTokensUsed` 스냅샷을 사용한 폴백으로 남아 있습니다. 호출별 사용 상세 정보가 제공될 때까지 TokenTracker는 Grok 비용을 추정합니다.
|
|
213
214
|
>
|
|
214
215
|
> 언제든 `tokentracker status`로 각 통합의 상태를 확인할 수 있습니다. `skipped`로 표시되면 `detail` 컬럼이 이유를 설명합니다 (예: 도구 CLI가 `PATH`에 없음, 설정 읽기 불가).
|
|
@@ -225,7 +226,7 @@ brew install xiufengsun/tokentracker/tokentracker
|
|
|
225
226
|
|
|
226
227
|
| 기능 | **[TokenTracker](https://github.com/xiufengsun/TokenTracker)** | **[ccusage](https://github.com/ccusage/ccusage)** | **[Tokscale](https://github.com/junhoyeo/tokscale)** |
|
|
227
228
|
|---|:---:|:---:|:---:|
|
|
228
|
-
| **지원하는 AI 도구 수** | **
|
|
229
|
+
| **지원하는 AI 도구 수** | **40** | 멀티 에이전트 지원 | 멀티 에이전트 지원 |
|
|
229
230
|
| **기본 인터페이스** | 네이티브 데스크톱 앱 & 웹 대시보드 | 터미널 CLI | 터미널 TUI & CLI |
|
|
230
231
|
| **로컬 우선 분석** | ✅ | ✅ | ✅ |
|
|
231
232
|
| **네이티브 데스크톱 앱** | ✅ macOS, Windows, Linux | ❌ | ❌ |
|
|
@@ -241,7 +242,7 @@ brew install xiufengsun/tokentracker/tokentracker
|
|
|
241
242
|
|
|
242
243
|
```mermaid
|
|
243
244
|
flowchart LR
|
|
244
|
-
A["AI coding tools<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"]
|
|
245
|
+
A["AI coding tools<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 · MiniMax Code"]
|
|
245
246
|
A -->|hooks trigger| B[Token Tracker]
|
|
246
247
|
B -->|parse logs<br/>30-min UTC buckets| C[(Local SQLite)]
|
|
247
248
|
C --> D[Web Dashboard]
|
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 **
|
|
9
|
+
An accurate, local-first token usage and cost dashboard for **40 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
|
[](https://www.npmjs.com/package/tokentracker-cli)
|
|
12
12
|
[](https://www.npmjs.com/package/tokentracker-cli)
|
|
@@ -147,7 +147,7 @@ An Arch `PKGBUILD` for a local pacman install lives in `TokenTrackerLinux/packag
|
|
|
147
147
|
|
|
148
148
|
## ✨ Features
|
|
149
149
|
|
|
150
|
-
- 🔌 **
|
|
150
|
+
- 🔌 **40 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, OmO, 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, MiniMax Code
|
|
151
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
|
|
@@ -272,6 +272,7 @@ An Arch `PKGBUILD` for a local pacman install lives in `TokenTrackerLinux/packag
|
|
|
272
272
|
| **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. |
|
|
273
273
|
| **AnythingLLM Desktop** | ✅ Auto | Passive SQLite reader (`anythingllm-desktop/storage/anythingllm.db`; reads per-message token metrics only, never prompts or responses) |
|
|
274
274
|
| **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$`. |
|
|
275
|
+
| **MiniMax Code** | ✅ Auto | Passive reader (`~/.minimax/v2/sessions/YYYY/MM/DD/<session>/messages.jsonl`). Dedupes on `message_id`, records the routed upstream model per message, and ignores the always-zero `usage.cost` in favor of normal pricing. Override the directory with `TOKENTRACKER_MINIMAX_HOME` |
|
|
275
276
|
| **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. |
|
|
276
277
|
| **DeepSeek Harness** | ✅ Auto | Passive session reader (`~/.dsh/sessions/**/session.jsonl[.zstd]`; parses session header and assistant events, handles multi-frame zstd decompression) |
|
|
277
278
|
| **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 |
|
|
@@ -281,7 +282,7 @@ An Arch `PKGBUILD` for a local pacman install lives in `TokenTrackerLinux/packag
|
|
|
281
282
|
> - **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.
|
|
282
283
|
> - **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.
|
|
283
284
|
> - **OmO** — passive session scan only (`~/.omo/agent/sessions/**/*.jsonl`, including nested subagent transcripts). No plugin or hook is installed. Path overrides, in precedence order: `TOKENTRACKER_OMO_AGENT_DIR`, then `TOKENTRACKER_OMO_HOME` / `OMO_HOME` (appended with `/agent`). `PI_CONFIG_DIR` and `PI_CODING_AGENT_DIR` are not honored: those belong to pi/omp.
|
|
284
|
-
> - **Passive readers** (Cursor, Kiro, Hermes, Kimi Code, Copilot, **Grok Build**, **OmO**, **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.
|
|
285
|
+
> - **Passive readers** (Cursor, Kiro, Hermes, Kimi Code, Copilot, **Grok Build**, **OmO**, **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**, **MiniMax Code**, **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.
|
|
285
286
|
> - **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.
|
|
286
287
|
>
|
|
287
288
|
> 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).
|
|
@@ -298,7 +299,7 @@ Missing your tool? [Open an issue](https://github.com/xiufengsun/TokenTracker/is
|
|
|
298
299
|
|
|
299
300
|
| Capability | **[TokenTracker](https://github.com/xiufengsun/TokenTracker)** | **[ccusage](https://github.com/ccusage/ccusage)** | **[Tokscale](https://github.com/junhoyeo/tokscale)** |
|
|
300
301
|
|---|:---:|:---:|:---:|
|
|
301
|
-
| **AI tools supported** | **
|
|
302
|
+
| **AI tools supported** | **40** | Multi-agent | Multi-agent |
|
|
302
303
|
| **Primary interface** | Desktop apps & web dashboard | Terminal CLI | Terminal TUI & CLI |
|
|
303
304
|
| **Local analysis** | ✅ | ✅ | ✅ |
|
|
304
305
|
| **Native desktop apps** | ✅ macOS, Windows, Linux | ❌ | ❌ |
|
|
@@ -314,7 +315,7 @@ Missing your tool? [Open an issue](https://github.com/xiufengsun/TokenTracker/is
|
|
|
314
315
|
|
|
315
316
|
```mermaid
|
|
316
317
|
flowchart LR
|
|
317
|
-
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 · OmO · pi · Dots · Craft · Roo · Zed · Goose · Droid · Mimo · ZCode · Qoder · AnythingLLM · Claude Science · DeepSeek Harness · TRAE Work CN · LM Studio · Unsloth Studio · Devin CLI"]
|
|
318
|
+
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 · OmO · pi · Dots · Craft · Roo · Zed · Goose · Droid · Mimo · ZCode · Qoder · AnythingLLM · Claude Science · DeepSeek Harness · TRAE Work CN · LM Studio · Unsloth Studio · Devin CLI · MiniMax Code"]
|
|
318
319
|
A -->|hooks trigger| B[Token Tracker]
|
|
319
320
|
B -->|parse logs<br/>30-min UTC buckets| C[(Local SQLite)]
|
|
320
321
|
C --> D[Web Dashboard]
|
|
@@ -367,6 +368,7 @@ Most users never need this — defaults are sensible. For advanced setups:
|
|
|
367
368
|
| `TOKENTRACKER_LMSTUDIO_HOME` | Override the LM Studio data directory used by the passive server-log reader | `~/.lmstudio` |
|
|
368
369
|
| `TOKENTRACKER_UNSLOTH_DB` | Override the Unsloth Studio database file used by the passive usage reader | `$UNSLOTH_STUDIO_HOME/studio.db` |
|
|
369
370
|
| `TOKENTRACKER_DEVIN_DB` | Override the Devin CLI database file used by the passive usage reader | `$XDG_DATA_HOME/devin/cli/sessions.db` |
|
|
371
|
+
| `TOKENTRACKER_MINIMAX_HOME` | Override the MiniMax Code data directory used by the passive session reader | `~/.minimax` |
|
|
370
372
|
|
|
371
373
|
### 🐧 Windows Subsystem for Linux (WSL) Auto-Discovery
|
|
372
374
|
|
package/README.zh-CN.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
### 跨所有 CLI,看清你到底在 AI 上花了多少钱
|
|
8
8
|
|
|
9
|
-
自动采集 **
|
|
9
|
+
自动采集 **40 款 AI 编码工具** 的 token 用量,全程本地聚合,用一套漂亮的 Dashboard 看真实成本与趋势。不需要云账号、不需要 API Key、不需要任何配置 —— 一条命令搞定。
|
|
10
10
|
|
|
11
11
|
[](https://www.npmjs.com/package/tokentracker-cli)
|
|
12
12
|
[](https://www.npmjs.com/package/tokentracker-cli)
|
|
@@ -90,7 +90,7 @@ brew install xiufengsun/tokentracker/tokentracker
|
|
|
90
90
|
|
|
91
91
|
## ✨ 特性
|
|
92
92
|
|
|
93
|
-
- 🔌 **开箱即用支持
|
|
93
|
+
- 🔌 **开箱即用支持 40 款 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、OmO、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、MiniMax Code
|
|
94
94
|
- 🏠 **本地优先** —— 跑在本地。直接在本地机器解析日志,无需账号或 API Key。
|
|
95
95
|
- 🚀 **零配置** —— 首次运行自动安装所有 hook。30 秒从零到 Dashboard
|
|
96
96
|
- 📊 **漂亮的 Dashboard** —— 用量趋势、按模型的成本分解、GitHub 风格活跃度热力图、按项目归因
|
|
@@ -203,6 +203,7 @@ brew install xiufengsun/tokentracker/tokentracker
|
|
|
203
203
|
| **Unsloth Studio** | ✅ 自动 | 被动读取 `$UNSLOTH_STUDIO_HOME/studio.db`(默认 `~/.unsloth/studio/studio.db`);只投影标量 `contextUsage` 元数据和不含内容的 `api_usage_events`。不会读取提示词、回复、附件、API subject、凭据或训练指标;本地路由成本为 $0,已知付费 provider 路由按实际响应模型估算 token 成本。 |
|
|
204
204
|
| **AnythingLLM Desktop** | ✅ 自动 | 被动 SQLite 读取(`anythingllm-desktop/storage/anythingllm.db`;只读取每条消息的 token 指标,不读取 prompt 或回复) |
|
|
205
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$` 读取。 |
|
|
206
|
+
| **MiniMax Code** | ✅ 自动 | 被动读取(`~/.minimax/v2/sessions/YYYY/MM/DD/<session>/messages.jsonl`)。按 `message_id` 去重,按消息记录实际路由到的上游模型;`usage.cost` 恒为 0,不采用,按常规定价计算。可用 `TOKENTRACKER_MINIMAX_HOME` 覆盖目录 |
|
|
206
207
|
| **Claude Science** | ✅ 自动 | 被动 SQLite 读取(`~/.claude-science/operon-cli.db`;只读取 `frames` 表的 token 计数,不读取 prompt、产物或研究内容)。没有原生 Windows 版——Windows 上该应用运行在 WSL 内,从 WSL 中读取。 |
|
|
207
208
|
| **DeepSeek Harness** | ✅ 自动 | 被动读取会话日志(`~/.dsh/sessions/**/session.jsonl[.zstd]`;解析会话头部与 assistant 事件,支持多帧 zstd 解压) |
|
|
208
209
|
| **TRAE Work CN** | ✅ 自动 | **需要显式开启:设置 `TOKENTRACKER_TRAE_CN_USAGE=1`。** 读取用量会把本地保存的登录授权发送到 TRAE 的内部 API,因此在你开启之前不会发出任何请求。开启后:仅在存在本地 TRAE Work CN 登录授权的可执行非后台同步期间,读取 macOS / Windows 本地登录应用的 session-token 用量;内部 API 可能变化 |
|
|
@@ -211,7 +212,7 @@ brew install xiufengsun/tokentracker/tokentracker
|
|
|
211
212
|
> - **基于 hook 的工具**(Claude Code、Codex、AStudio、Gemini、Every Code、**CodeBuddy**、**WorkBuddy**、**Grok Build**)—— 我们把 SessionEnd hook 或 TOML notify 条目写入它们自己的配置文件
|
|
212
213
|
> - **基于插件的工具**(OpenCode、**OpenClaw**)—— 插件随 npm 包一起分发。OpenClaw 的 session plugin 位于 `~/.tokentracker/tracker/openclaw-plugin/openclaw-session-sync/`;TokenTracker 会通过 OpenClaw 自己的 CLI 挂接并启用它,然后写入 `hooks.allowConversationAccess=true`,让 OpenClaw 放行触发同步的会话结束事件。无需下载、无需拖拽
|
|
213
214
|
> - **oh-my-pi** —— 被动会话扫描始终是计费/token 的事实来源(`~/.omp/agent/sessions/**/*.jsonl`)。检测到 OMP 时,`tokentracker init` 还会写入托管的 notify 扩展到 `~/.omp/agent/extensions/tokentracker-notify.ts`,以便回合结束后近实时触发同步。仅当文件带有 TokenTracker 托管标记时才视为我们拥有:同名用户自写扩展绝不会被覆盖或删除。`tokentracker uninstall` 也仅在文件仍为托管状态时删除它。
|
|
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` 聚合量,而不会猜测请求模型
|
|
215
|
+
> - **被动读取类**(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**、**MiniMax Code**、**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` 聚合量,而不会猜测请求模型
|
|
215
216
|
> - **Grok Build 估算说明** —— Grok 当前本地遥测提供 `updates.jsonl` 里的累计 `totalTokens`,但还没有稳定的输入/输出/cache 拆分;`signals.json` 仍作为 `contextTokensUsed` 快照兜底。所以在 Grok 提供按调用粒度的用量明细之前,TokenTracker 对 Grok 成本仍是估算值
|
|
216
217
|
>
|
|
217
218
|
> 任何时候都可以用 `tokentracker status` 查看每个集成的状态。如果显示 `skipped`,`detail` 列会解释原因(例如某工具 CLI 不在 `PATH` 上、config 不可读等)。
|
|
@@ -228,7 +229,7 @@ brew install xiufengsun/tokentracker/tokentracker
|
|
|
228
229
|
|
|
229
230
|
| 功能维度 | **[TokenTracker](https://github.com/xiufengsun/TokenTracker)** | **[ccusage](https://github.com/ccusage/ccusage)** | **[Tokscale](https://github.com/junhoyeo/tokscale)** |
|
|
230
231
|
|---|:---:|:---:|:---:|
|
|
231
|
-
| **支持的 AI 工具数** | **
|
|
232
|
+
| **支持的 AI 工具数** | **40** | 多 Agent / 工具支持 | 多 Agent / 工具支持 |
|
|
232
233
|
| **主要交互方式** | 原生桌面 App 与 Web Dashboard | 终端命令行(CLI) | 终端 TUI 与 CLI |
|
|
233
234
|
| **本地优先分析** | ✅ | ✅ | ✅ |
|
|
234
235
|
| **原生桌面 App** | ✅ macOS、Windows、Linux | ❌ | ❌ |
|
|
@@ -244,7 +245,7 @@ brew install xiufengsun/tokentracker/tokentracker
|
|
|
244
245
|
|
|
245
246
|
```mermaid
|
|
246
247
|
flowchart LR
|
|
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"]
|
|
248
|
+
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 · MiniMax Code"]
|
|
248
249
|
A -->|hook 触发| B[Token Tracker]
|
|
249
250
|
B -->|解析日志<br/>30 分钟 UTC 桶| C[(本地 SQLite)]
|
|
250
251
|
C --> D[Web Dashboard]
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{m as n,d as F,j as i}from"./__vite-browser-external-
|
|
1
|
+
import{m as n,d as F,j as i}from"./__vite-browser-external-OxXsgcds.js";import{Z as I}from"./zap-CucEQWyU.js";import{F as G}from"./flame-DhPzeD5Z.js";const k={name:"blocks",size:24,node:[["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"}]]};k.node;const N=n(k);const u={name:"brain",size:24,node:[["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"}]]};u.node;const S=n(u);const v={name:"calendar-days",size:24,node:[["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"}]]};v.node;const V=n(v);const M={name:"crown",size:24,node:[["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"}]]};M.node;const Z=n(M);const b={name:"feather",size:24,node:[["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"}]]};b.node;const O=n(b);const _={name:"folder-git-2",size:24,node:[["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"}]]};_.node;const P=n(_);const x={name:"footprints",size:24,node:[["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"}]]};x.node;const X=n(x);const w={name:"heart",size:24,node:[["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"}]]};w.node;const K=n(w);const z={name:"medal",size:24,node:[["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"}]]};z.node;const U=n(z);const D={name:"moon-star",size:24,node:[["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"}]]};D.node;const W=n(D);const $={name:"rocket",size:24,node:[["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"}]]};$.node;const Y=n($);const A={name:"shield-check",size:24,node:[["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"}]]};A.node;const J=n(A);const j={name:"trending-up",size:24,node:[["path",{d:"M16 7h6v6",key:"box55l"}],["path",{d:"m22 7-8.5 8.5-5-5L2 17",key:"1t1m79"}]]};j.node;const Q=n(j),E=[{id:"token_titan",scope:"cloud",icon:Z,format:"tokens",art:"/achievements/token-titan.png"},{id:"big_day",scope:"cloud",icon:I,format:"tokens",art:"/achievements/big-day.png"},{id:"wordsmith",scope:"cloud",icon:O,format:"tokens",art:"/achievements/wordsmith.png"},{id:"marathoner",scope:"cloud",icon:X,format:"days",art:"/achievements/marathoner.png"},{id:"streak",scope:"cloud",icon:G,format:"days",art:"/achievements/streak.png"},{id:"weekend_warrior",scope:"cloud",icon:V,format:"days",art:"/achievements/weekend-warrior.png"},{id:"momentum",scope:"cloud",icon:Q,format:"multiplier",art:"/achievements/momentum.png"},{id:"polyglot",scope:"cloud",icon:S,format:"count",art:"/achievements/polyglot.png"},{id:"trendsetter",scope:"cloud",icon:Y,format:"count",art:"/achievements/trendsetter.png"},{id:"multitool",scope:"cloud",icon:N,format:"count",art:"/achievements/multitool.png"},{id:"podium",scope:"cloud",icon:U,format:"rank",art:"/achievements/podium.png"},{id:"veteran",scope:"cloud",icon:J,format:"days",art:"/achievements/veteran.png"},{id:"project_hopper",scope:"local",icon:P,format:"count",art:"/achievements/project-hopper.png"},{id:"project_devotion",scope:"local",icon:K,format:"tokens",art:"/achievements/project-devotion.png"},{id:"night_owl",scope:"local",icon:W,format:"count",art:"/achievements/night-owl.png"}],L=new Map(E.map(e=>[e.id,e])),m=new Map(E.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 ie(e){const a=(e||[]).filter(t=>t&&(t.tier||0)>=1&&L.has(t.id));return a.length===0?null:ee(a)[0]}const C={1:"bronze",2:"silver",3:"gold",4:"diamond"},re=["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[C[Math.min(e,4)]]||y}function se(e){return C[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:r,className:T=""}){const s=L.get(e),h=c||!a||a<1,o=te(a,h),d=t==="fill",l=d?"100%":g[t]||g.md,q=d?4:f[t]||f.md,[B,H]=F.useState(!1),p=s?.icon||null,R=!!s?.art&&!B;return i.jsx("span",{role:r?"img":void 0,"aria-label":r||void 0,"aria-hidden":r?void 0:!0,className:`inline-flex shrink-0 rounded-full ${T}`,style:{width:l,height:l,padding:q,background:`linear-gradient(135deg, ${o.rim[0]} 0%, ${o.ring} 42%, ${o.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:R?i.jsx("img",{src:s.art,alt:"",draggable:!1,loading:"lazy",onError:()=>H(!0),className:"h-full w-full rounded-full object-cover",style:h?{filter:"grayscale(1)",opacity:.55}:void 0}):i.jsx("span",{className:"flex h-full w-full items-center justify-center rounded-full",style:{background:`linear-gradient(145deg, ${o.face[0]}, ${o.face[1]})`},children:p?i.jsx(p,{size:d?64:Math.round(l*.5),color:o.glyph,strokeWidth:2}):null})})}export{le as A,E as B,K as H,Q as T,ae as a,de as b,L as c,re as d,ie 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-
|
|
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-
|
|
1
|
+
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/BadgeDetailModal-Sk2dHIkJ.js","assets/__vite-browser-external-OxXsgcds.js","assets/__vite-browser-external-5P7me0Qq.css","assets/react-C_5Bvle-.js","assets/AchievementBadge-pib0F8p8.js","assets/zap-CucEQWyU.js","assets/flame-DhPzeD5Z.js","assets/use-transform-DP6mCHUn.js","assets/use-reduced-motion-DyXrwS-h.js","assets/AchievementsSection-DG_-yxAt.js","assets/format-tokens-C4eAHkPw.js","assets/DialogTitle-BZb-SEg0.js","assets/composite-BxlyIOoF.js","assets/store-D3n2aJ7w.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-OxXsgcds.js";import{A as w}from"./AchievementsSection-DG_-yxAt.js";import{B as x}from"./AchievementBadge-pib0F8p8.js";import"./react-C_5Bvle-.js";import"./format-tokens-C4eAHkPw.js";import"./use-reduced-motion-DyXrwS-h.js";import"./zap-CucEQWyU.js";import"./flame-DhPzeD5Z.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-Sk2dHIkJ.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};
|
package/dashboard/dist/assets/{AchievementsSection-y5mok2w0.js → AchievementsSection-DG_-yxAt.js}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{m as $,j as s,c as l}from"./__vite-browser-external-
|
|
1
|
+
import{m as $,j as s,c as l}from"./__vite-browser-external-OxXsgcds.js";import{m as C}from"./react-C_5Bvle-.js";import{B as x,s as b,b as T,t as B,A as E,a as L}from"./AchievementBadge-pib0F8p8.js";import{f as D}from"./format-tokens-C4eAHkPw.js";import{u as I}from"./use-reduced-motion-DyXrwS-h.js";const v={name:"hard-drive",size:24,node:[["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"}]]};v.node;const z=$(v);function V(r,i){const t=Number(i)||0;switch(r){case"tokens":return D(t);case"rank":return`#${Math.round(t)}`;case"multiplier":return`${(Math.round(t*10)/10).toString()}×`;default:return Math.round(t).toLocaleString()}}function F(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:y="default",animateIn:k=!1,className:N=""}){const j=I(),u=k&&!j,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 w=y==="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)]",M=m==="lg"?"mx-0 sm:-mx-2":"-mx-2 sm:-mx-4",A=m==="lg"?"px-0":"px-1";return s.jsx("div",{className:`${M} grid justify-between gap-y-7 ${w} ${N}`,children:c.map(({entry:e,record:n},h)=>{const a=(n.tier||0)>=1,f=l(T(e.id,"name")),d=a?B(n.tier):null,_=F(n);return s.jsxs(C.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 ${A} 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(E,{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(z,{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:L[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(_*100)}%`}})}):null})]},e.id)})})}export{K as A,F as b,V as f,q as l};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{r as L,j as a,b4 as De,c as h,e as ye,X as Ie}from"./__vite-browser-external-CTGqjrCe.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-Gft01KYk.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-kRsjlB3p.js";import{u as Qe,a as z}from"./use-transform-qRwUetOk.js";import{u as et}from"./use-reduced-motion-CCuEY-79.js";import{l as ce,f as ue,b as tt}from"./AchievementsSection-y5mok2w0.js";import{D as nt,a as rt,b as at,c as st,d as it,e as ot}from"./DialogTitle-Ckzf1tdi.js";import"./zap-CS80c_uN.js";import"./flame-D4yFLV1L.js";import"./format-tokens-CBUNbBvp.js";import"./composite-B2s880f5.js";import"./store-CgJHe9jU.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-OxXsgcds.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-C_5Bvle-.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-pib0F8p8.js";import{u as Qe,a as z}from"./use-transform-DP6mCHUn.js";import{u as et}from"./use-reduced-motion-DyXrwS-h.js";import{l as ce,f as ue,b as tt}from"./AchievementsSection-DG_-yxAt.js";import{D as nt,a as rt,b as at,c as st,d as it,e as ot}from"./DialogTitle-BZb-SEg0.js";import"./zap-CucEQWyU.js";import"./flame-DhPzeD5Z.js";import"./format-tokens-C4eAHkPw.js";import"./composite-BxlyIOoF.js";import"./store-D3n2aJ7w.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};
|