ccgauge 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/.next/standalone/.next/BUILD_ID +1 -1
  2. package/.next/standalone/.next/app-build-manifest.json +55 -55
  3. package/.next/standalone/.next/app-path-routes-manifest.json +8 -8
  4. package/.next/standalone/.next/build-manifest.json +3 -3
  5. package/.next/standalone/.next/required-server-files.json +3 -3
  6. package/.next/standalone/.next/server/app/_not-found/page.js +2 -2
  7. package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  8. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  9. package/.next/standalone/.next/server/app/api/blocks/route.js +1 -1
  10. package/.next/standalone/.next/server/app/api/blocks/route_client-reference-manifest.js +1 -1
  11. package/.next/standalone/.next/server/app/api/pricing/route.js +1 -1
  12. package/.next/standalone/.next/server/app/api/pricing/route_client-reference-manifest.js +1 -1
  13. package/.next/standalone/.next/server/app/api/projects/route.js +1 -1
  14. package/.next/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
  15. package/.next/standalone/.next/server/app/api/scan/route.js +1 -1
  16. package/.next/standalone/.next/server/app/api/scan/route_client-reference-manifest.js +1 -1
  17. package/.next/standalone/.next/server/app/api/sessions/route.js +1 -1
  18. package/.next/standalone/.next/server/app/api/sessions/route_client-reference-manifest.js +1 -1
  19. package/.next/standalone/.next/server/app/api/usage/route.js +1 -1
  20. package/.next/standalone/.next/server/app/api/usage/route_client-reference-manifest.js +1 -1
  21. package/.next/standalone/.next/server/app/models/page.js +2 -2
  22. package/.next/standalone/.next/server/app/models/page.js.nft.json +1 -1
  23. package/.next/standalone/.next/server/app/models/page_client-reference-manifest.js +1 -1
  24. package/.next/standalone/.next/server/app/page.js +2 -2
  25. package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
  26. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  27. package/.next/standalone/.next/server/app/projects/[id]/page.js +2 -2
  28. package/.next/standalone/.next/server/app/projects/[id]/page.js.nft.json +1 -1
  29. package/.next/standalone/.next/server/app/projects/[id]/page_client-reference-manifest.js +1 -1
  30. package/.next/standalone/.next/server/app/projects/page.js +2 -2
  31. package/.next/standalone/.next/server/app/projects/page.js.nft.json +1 -1
  32. package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
  33. package/.next/standalone/.next/server/app/sessions/[id]/page.js +2 -2
  34. package/.next/standalone/.next/server/app/sessions/[id]/page.js.nft.json +1 -1
  35. package/.next/standalone/.next/server/app/sessions/[id]/page_client-reference-manifest.js +1 -1
  36. package/.next/standalone/.next/server/app/sessions/page.js +2 -2
  37. package/.next/standalone/.next/server/app/sessions/page.js.nft.json +1 -1
  38. package/.next/standalone/.next/server/app/sessions/page_client-reference-manifest.js +1 -1
  39. package/.next/standalone/.next/server/app/settings/page.js +2 -2
  40. package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -1
  41. package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  42. package/.next/standalone/.next/server/app/usage/page.js +2 -2
  43. package/.next/standalone/.next/server/app/usage/page.js.nft.json +1 -1
  44. package/.next/standalone/.next/server/app/usage/page_client-reference-manifest.js +1 -1
  45. package/.next/standalone/.next/server/app-paths-manifest.json +8 -8
  46. package/.next/standalone/.next/server/chunks/116.js +1 -1
  47. package/.next/standalone/.next/server/chunks/144.js +21 -0
  48. package/.next/standalone/.next/server/chunks/328.js +1 -0
  49. package/.next/standalone/.next/server/chunks/508.js +1 -0
  50. package/.next/standalone/.next/server/chunks/930.js +2 -2
  51. package/.next/standalone/.next/server/functions-config-manifest.json +4 -4
  52. package/.next/standalone/.next/server/middleware-build-manifest.js +1 -1
  53. package/.next/standalone/.next/server/pages/500.html +1 -1
  54. package/.next/standalone/.next/static/chunks/327-e20abd872315fef4.js +1 -0
  55. package/.next/standalone/.next/static/chunks/app/_not-found/{page-7673b5ac2a6b0f9e.js → page-41dcfc1f77804c54.js} +1 -1
  56. package/.next/standalone/.next/static/chunks/app/layout-b79aa5837ed28479.js +1 -0
  57. package/.next/standalone/.next/static/chunks/app/models/{page-5e67aa3f2cf52ce0.js → page-f5bc9e42fd99f1fa.js} +1 -1
  58. package/.next/standalone/.next/static/chunks/app/{page-a10e61d14cef6873.js → page-bebe32a605b907bc.js} +1 -1
  59. package/.next/standalone/.next/static/chunks/app/projects/[id]/page-d11fdad7ed247804.js +1 -0
  60. package/.next/standalone/.next/static/chunks/app/projects/page-234a446af5011898.js +1 -0
  61. package/.next/standalone/.next/static/chunks/app/sessions/[id]/page-d11fdad7ed247804.js +1 -0
  62. package/.next/standalone/.next/static/chunks/app/sessions/page-234a446af5011898.js +1 -0
  63. package/.next/standalone/.next/static/chunks/app/settings/page-a9922bc0df226b45.js +1 -0
  64. package/.next/standalone/.next/static/chunks/app/usage/page-8841853999af6b39.js +1 -0
  65. package/.next/standalone/.next/static/chunks/main-app-f109f05b87bf48c1.js +1 -0
  66. package/.next/standalone/.next/static/css/1853d325765daf80.css +3 -0
  67. package/.next/standalone/package.json +2 -2
  68. package/.next/standalone/server.js +1 -1
  69. package/README.md +24 -56
  70. package/README.zh-CN.md +23 -55
  71. package/bin/cli.mjs +6 -6
  72. package/package.json +2 -2
  73. package/.next/standalone/.next/server/chunks/204.js +0 -21
  74. package/.next/standalone/.next/server/chunks/212.js +0 -1
  75. package/.next/standalone/.next/static/chunks/327-bd5c008a8d095a96.js +0 -1
  76. package/.next/standalone/.next/static/chunks/app/layout-39d16cd1c7e3ab9e.js +0 -1
  77. package/.next/standalone/.next/static/chunks/app/projects/[id]/page-7a8f3918f5e04708.js +0 -1
  78. package/.next/standalone/.next/static/chunks/app/projects/page-316e2d12f1d2d345.js +0 -1
  79. package/.next/standalone/.next/static/chunks/app/sessions/[id]/page-7a8f3918f5e04708.js +0 -1
  80. package/.next/standalone/.next/static/chunks/app/sessions/page-316e2d12f1d2d345.js +0 -1
  81. package/.next/standalone/.next/static/chunks/app/settings/page-60f64e8f728d39ce.js +0 -1
  82. package/.next/standalone/.next/static/chunks/app/usage/page-ebd4b1f44eb68921.js +0 -1
  83. package/.next/standalone/.next/static/chunks/main-app-7bfaffb06750059f.js +0 -1
  84. package/.next/standalone/.next/static/css/e24bab5baa9273ba.css +0 -3
  85. /package/.next/standalone/.next/static/{Eha40QJ9Gc01FvsqQomro → kSxC61RuI0if1xBcXSx-q}/_buildManifest.js +0 -0
  86. /package/.next/standalone/.next/static/{Eha40QJ9Gc01FvsqQomro → kSxC61RuI0if1xBcXSx-q}/_ssgManifest.js +0 -0
package/README.md CHANGED
@@ -1,22 +1,22 @@
1
1
  <div align="center">
2
2
 
3
- # ccdsb
3
+ # ccgauge
4
4
 
5
- **C**laude **C**ode **D**a**s**h**b**oard — a zero-config local web UI for your Claude Code token usage and cost.
5
+ Local web dashboard for your Claude Code token usage and cost. Zero-config.
6
6
 
7
- [![npm version](https://img.shields.io/npm/v/ccdsb?color=C96442&style=flat-square)](https://www.npmjs.com/package/ccdsb)
8
- [![license](https://img.shields.io/npm/l/ccdsb?color=C96442&style=flat-square)](./LICENSE)
9
- [![node](https://img.shields.io/node/v/ccdsb?color=C96442&style=flat-square)](#)
7
+ [![npm version](https://img.shields.io/npm/v/ccgauge?color=C96442&style=flat-square)](https://www.npmjs.com/package/ccgauge)
8
+ [![license](https://img.shields.io/npm/l/ccgauge?color=C96442&style=flat-square)](./LICENSE)
9
+ [![node](https://img.shields.io/node/v/ccgauge?color=C96442&style=flat-square)](#)
10
10
 
11
11
  [English](./README.md) · [简体中文](./README.zh-CN.md)
12
12
 
13
13
  </div>
14
14
 
15
15
  ```bash
16
- npx ccdsb
16
+ npx ccgauge
17
17
  ```
18
18
 
19
- That's it. ccdsb scans `~/.claude/projects/` (and `~/.config/claude/projects/`), reads the JSONL files, computes token usage + USD cost + cache savings, and opens a dashboard in your browser. **Data never leaves your machine.**
19
+ That's it. ccgauge scans `~/.claude/projects/` (and `~/.config/claude/projects/`), reads the JSONL files, computes token usage + USD cost + cache savings, and opens a dashboard in your browser. **Data never leaves your machine.**
20
20
 
21
21
  ![Overview — English / Dark](./docs/screenshots/overview-en-dark.png)
22
22
 
@@ -24,7 +24,7 @@ That's it. ccdsb scans `~/.claude/projects/` (and `~/.config/claude/projects/`),
24
24
 
25
25
  ## Why
26
26
 
27
- [ccusage](https://github.com/ryoppippi/ccusage) (the de-facto standard) is a great terminal CLI but it's a wall of numbers. ccdsb gives you the same data with charts, drill-down by project / session / model, a live 5-hour-block countdown, and a clear **"saved by cache"** KPI — all in a polished local web UI. Bilingual (English + 中文), light + dark themes, completely offline.
27
+ [ccusage](https://github.com/ryoppippi/ccusage) (the de-facto standard) is a great terminal CLI but it's a wall of numbers. ccgauge gives you the same data with charts, drill-down by project / session / model, a live 5-hour-block countdown, and a clear **"saved by cache"** KPI — all in a polished local web UI. Bilingual (English + 中文), light + dark themes, completely offline.
28
28
 
29
29
  ## Features
30
30
 
@@ -41,55 +41,25 @@ That's it. ccdsb scans `~/.claude/projects/` (and `~/.config/claude/projects/`),
41
41
  - **Export** — CSV download of the request log
42
42
  - **100% local** — read-only access to JSONL files, no telemetry, no network calls
43
43
 
44
- ## Screenshots
45
-
46
- ### Overview — English · Dark
47
-
48
- ![](./docs/screenshots/overview-en-dark.png)
49
-
50
- ### Overview — 中文 · Light
51
-
52
- ![](./docs/screenshots/overview-zh-light.png)
53
-
54
- ### Usage — filters, stacked trend, request log
55
-
56
- ![](./docs/screenshots/usage-en-dark.png)
57
-
58
- ### Sessions — every conversation, sorted by recency
59
-
60
- ![](./docs/screenshots/sessions-en-dark.png)
61
-
62
- ### Projects — per-`cwd` spend cards
63
-
64
- ![](./docs/screenshots/projects-en-dark.png)
65
-
66
- ### Models — side-by-side cost / cache / pricing
67
-
68
- ![](./docs/screenshots/models-en-dark.png)
69
-
70
- ### Settings — language / theme / data sources / pricing table
71
-
72
- ![](./docs/screenshots/settings-zh-light.png)
73
-
74
44
  ## Install / Run
75
45
 
76
46
  ```bash
77
47
  # zero-install one-shot (recommended)
78
- npx ccdsb
48
+ npx ccgauge
79
49
 
80
50
  # global install
81
- npm i -g ccdsb && ccdsb
82
- pnpm i -g ccdsb && ccdsb
83
- yarn global add ccdsb && ccdsb
51
+ npm i -g ccgauge && ccgauge
52
+ pnpm i -g ccgauge && ccgauge
53
+ yarn global add ccgauge && ccgauge
84
54
 
85
55
  # dlx
86
- pnpm dlx ccdsb
56
+ pnpm dlx ccgauge
87
57
  ```
88
58
 
89
59
  ### Options
90
60
 
91
61
  ```
92
- ccdsb [options]
62
+ ccgauge [options]
93
63
 
94
64
  -p, --port <port> preferred port (default: 3737)
95
65
  -h, --host <host> bind host (default: 127.0.0.1)
@@ -100,22 +70,22 @@ ccdsb [options]
100
70
  --help show help
101
71
  ```
102
72
 
103
- If `3737` is taken ccdsb falls back to the next available port automatically.
73
+ If `3737` is taken ccgauge falls back to the next available port automatically.
104
74
 
105
75
  ### Environment variables
106
76
 
107
- | Variable | Effect |
108
- | -------------------- | ------------------------------------------------------------------- |
109
- | `CCDSB_CONFIG_DIR` | Use `<dir>/projects` as a data source (in addition to defaults) |
110
- | `CLAUDE_CONFIG_DIR` | Same as above (compatible with Claude Code 1.0.30+) |
77
+ | Variable | Effect |
78
+ | --------------------- | ------------------------------------------------------------------- |
79
+ | `CCGAUGE_CONFIG_DIR` | Use `<dir>/projects` as a data source (in addition to defaults) |
80
+ | `CLAUDE_CONFIG_DIR` | Same as above (compatible with Claude Code 1.0.30+) |
111
81
 
112
82
  ## Develop
113
83
 
114
84
  This repo is also a working Next.js project — you can run the dashboard against your live data while iterating on the code.
115
85
 
116
86
  ```bash
117
- git clone https://github.com/chengzuopeng/ccdsb.git
118
- cd ccdsb
87
+ git clone https://github.com/chengzuopeng/ccgauge.git
88
+ cd ccgauge
119
89
  pnpm install
120
90
  pnpm dev # http://localhost:3737
121
91
  ```
@@ -135,13 +105,13 @@ To produce the npm-publishable artifact:
135
105
 
136
106
  ```bash
137
107
  pnpm build
138
- node bin/cli.mjs # exact same entrypoint as `npx ccdsb`
108
+ node bin/cli.mjs # exact same entrypoint as `npx ccgauge`
139
109
  ```
140
110
 
141
111
  To preview what would be published:
142
112
 
143
113
  ```bash
144
- pnpm pack # writes ccdsb-<version>.tgz; tar -tzf to inspect
114
+ pnpm pack # writes ccgauge-<version>.tgz; tar -tzf to inspect
145
115
  ```
146
116
 
147
117
  ## Publish
@@ -159,9 +129,7 @@ pnpm publish --access public
159
129
  2. Once the server responds, it [`open()`](https://github.com/sindresorhus/open)s the browser to that URL.
160
130
  3. The Next.js server-side code in `lib/data-loader/scan.ts` reads `~/.claude/projects/**/*.jsonl`, parses every `assistant` message, dedups via `(message.id, requestId)`, and aggregates by day / model / project / session / 5h-block.
161
131
  4. Pricing is from a built-in snapshot of Anthropic's published rates (12 models). Unknown models fall back to the same family's latest rate.
162
- 5. i18n + theme: Cookie-driven SSR + `localStorage` mirror + an inline no-flash script in `<head>`.
163
-
164
- See [PLAN.md](./PLAN.md) for the full design rationale, data-source investigation, and competitive analysis.
132
+ 5. i18n + theme: cookie-driven SSR + `localStorage` mirror + an inline no-flash script in `<head>`.
165
133
 
166
134
  ## License
167
135
 
package/README.zh-CN.md CHANGED
@@ -1,22 +1,22 @@
1
1
  <div align="center">
2
2
 
3
- # ccdsb
3
+ # ccgauge
4
4
 
5
- **C**laude **C**ode **D**a**s**h**b**oard —— Claude Code 用量本地看板,零配置开箱即用。
5
+ Claude Code 用量本地看板,零配置开箱即用。
6
6
 
7
- [![npm version](https://img.shields.io/npm/v/ccdsb?color=C96442&style=flat-square)](https://www.npmjs.com/package/ccdsb)
8
- [![license](https://img.shields.io/npm/l/ccdsb?color=C96442&style=flat-square)](./LICENSE)
9
- [![node](https://img.shields.io/node/v/ccdsb?color=C96442&style=flat-square)](#)
7
+ [![npm version](https://img.shields.io/npm/v/ccgauge?color=C96442&style=flat-square)](https://www.npmjs.com/package/ccgauge)
8
+ [![license](https://img.shields.io/npm/l/ccgauge?color=C96442&style=flat-square)](./LICENSE)
9
+ [![node](https://img.shields.io/node/v/ccgauge?color=C96442&style=flat-square)](#)
10
10
 
11
11
  [English](./README.md) · [简体中文](./README.zh-CN.md)
12
12
 
13
13
  </div>
14
14
 
15
15
  ```bash
16
- npx ccdsb
16
+ npx ccgauge
17
17
  ```
18
18
 
19
- 就这一行。ccdsb 会扫描 `~/.claude/projects/`(以及 `~/.config/claude/projects/`)下的 JSONL 文件,计算 token 用量、美元成本与缓存节省,然后在浏览器里打开看板。**所有数据全程不离开你的电脑。**
19
+ 就这一行。ccgauge 会扫描 `~/.claude/projects/`(以及 `~/.config/claude/projects/`)下的 JSONL 文件,计算 token 用量、美元成本与缓存节省,然后在浏览器里打开看板。**所有数据全程不离开你的电脑。**
20
20
 
21
21
  ![概览 — 中文 / Light](./docs/screenshots/overview-zh-light.png)
22
22
 
@@ -24,7 +24,7 @@ npx ccdsb
24
24
 
25
25
  ## 为什么写这个
26
26
 
27
- 社区已有的 [ccusage](https://github.com/ryoppippi/ccusage) 是终端 CLI 标杆,但它给你的是一墙的数字。ccdsb 用同一份数据画出图表、按项目 / 会话 / 模型分维度下钻、5 小时 block 实时倒计时,还把"缓存节省了多少钱"单独做成一张 KPI 卡 —— 全部塞进一个本地的现代 Web 看板。中英双语、亮暗双主题、完全离线。
27
+ 社区已有的 [ccusage](https://github.com/ryoppippi/ccusage) 是终端 CLI 标杆,但它给你的是一墙的数字。ccgauge 用同一份数据画出图表、按项目 / 会话 / 模型分维度下钻、5 小时 block 实时倒计时,还把"缓存节省了多少钱"单独做成一张 KPI 卡 —— 全部塞进一个本地的现代 Web 看板。中英双语、亮暗双主题、完全离线。
28
28
 
29
29
  ## 功能
30
30
 
@@ -41,55 +41,25 @@ npx ccdsb
41
41
  - **导出** —— 请求列表一键导出 CSV
42
42
  - **100% 本地** —— 只读访问 JSONL,零遥测,零网络调用
43
43
 
44
- ## 截图
45
-
46
- ### 概览 —— English · Dark
47
-
48
- ![](./docs/screenshots/overview-en-dark.png)
49
-
50
- ### 概览 —— 中文 · Light
51
-
52
- ![](./docs/screenshots/overview-zh-light.png)
53
-
54
- ### 用量 —— 筛选、堆叠趋势、请求明细
55
-
56
- ![](./docs/screenshots/usage-en-dark.png)
57
-
58
- ### 会话 —— 每场对话,按最近活跃排序
59
-
60
- ![](./docs/screenshots/sessions-en-dark.png)
61
-
62
- ### 项目 —— 按 `cwd` 聚合的花费卡片
63
-
64
- ![](./docs/screenshots/projects-en-dark.png)
65
-
66
- ### 模型 —— 成本 / 缓存 / 单价并排对比
67
-
68
- ![](./docs/screenshots/models-en-dark.png)
69
-
70
- ### 设置 —— 语言 / 主题 / 数据源 / 价格表
71
-
72
- ![](./docs/screenshots/settings-zh-light.png)
73
-
74
44
  ## 安装 / 运行
75
45
 
76
46
  ```bash
77
47
  # 一行运行(推荐)
78
- npx ccdsb
48
+ npx ccgauge
79
49
 
80
50
  # 全局安装后直接用
81
- npm i -g ccdsb && ccdsb
82
- pnpm i -g ccdsb && ccdsb
83
- yarn global add ccdsb && ccdsb
51
+ npm i -g ccgauge && ccgauge
52
+ pnpm i -g ccgauge && ccgauge
53
+ yarn global add ccgauge && ccgauge
84
54
 
85
55
  # pnpm dlx
86
- pnpm dlx ccdsb
56
+ pnpm dlx ccgauge
87
57
  ```
88
58
 
89
59
  ### 命令行选项
90
60
 
91
61
  ```
92
- ccdsb [options]
62
+ ccgauge [options]
93
63
 
94
64
  -p, --port <port> 首选端口(默认 3737)
95
65
  -h, --host <host> 绑定地址(默认 127.0.0.1)
@@ -100,22 +70,22 @@ ccdsb [options]
100
70
  --help 查看帮助
101
71
  ```
102
72
 
103
- 如果 `3737` 被占用,ccdsb 会自动顺延到下一个可用端口。
73
+ 如果 `3737` 被占用,ccgauge 会自动顺延到下一个可用端口。
104
74
 
105
75
  ### 环境变量
106
76
 
107
- | 变量 | 作用 |
108
- | ------------------- | ------------------------------------------------------------------- |
109
- | `CCDSB_CONFIG_DIR` | 把 `<dir>/projects` 也加入扫描路径(在默认路径之外) |
110
- | `CLAUDE_CONFIG_DIR` | 同上(与 Claude Code 1.0.30+ 兼容) |
77
+ | 变量 | 作用 |
78
+ | -------------------- | ------------------------------------------------------------------- |
79
+ | `CCGAUGE_CONFIG_DIR` | 把 `<dir>/projects` 也加入扫描路径(在默认路径之外) |
80
+ | `CLAUDE_CONFIG_DIR` | 同上(与 Claude Code 1.0.30+ 兼容) |
111
81
 
112
82
  ## 本地开发
113
83
 
114
84
  这个仓库本身就是一个能跑的 Next.js 工程,可以一边改代码一边看实时数据。
115
85
 
116
86
  ```bash
117
- git clone https://github.com/chengzuopeng/ccdsb.git
118
- cd ccdsb
87
+ git clone https://github.com/chengzuopeng/ccgauge.git
88
+ cd ccgauge
119
89
  pnpm install
120
90
  pnpm dev # http://localhost:3737
121
91
  ```
@@ -135,13 +105,13 @@ pnpm clean # rm -rf .next node_modules tsconfig.tsbuildinfo
135
105
 
136
106
  ```bash
137
107
  pnpm build
138
- node bin/cli.mjs # 入口与 npx ccdsb 完全一致
108
+ node bin/cli.mjs # 入口与 npx ccgauge 完全一致
139
109
  ```
140
110
 
141
111
  预览将要发布的内容:
142
112
 
143
113
  ```bash
144
- pnpm pack # 生成 ccdsb-<version>.tgz;用 tar -tzf 查看
114
+ pnpm pack # 生成 ccgauge-<version>.tgz;用 tar -tzf 查看
145
115
  ```
146
116
 
147
117
  ## 发布
@@ -161,8 +131,6 @@ pnpm publish --access public
161
131
  4. 单价用内置的 Anthropic 官价快照(12 个模型);遇到未知模型时,回退到同 family 的最新一档单价。
162
132
  5. i18n + 主题:cookie 驱动 SSR + `localStorage` 镜像 + `<head>` 注入一段同步执行的 no-flash 脚本。
163
133
 
164
- 完整的设计文档、数据源调研、竞品对比见 [PLAN.md](./PLAN.md)。
165
-
166
134
  ## 许可证
167
135
 
168
136
  MIT —— 详见 [LICENSE](./LICENSE)。
package/bin/cli.mjs CHANGED
@@ -19,7 +19,7 @@ const open = openMod.default;
19
19
 
20
20
  const program = new Command();
21
21
  program
22
- .name('ccdsb')
22
+ .name('ccgauge')
23
23
  .description(pkg.description ?? 'Claude Code Dashboard')
24
24
  .version(pkg.version ?? '0.0.0')
25
25
  .option('-p, --port <port>', 'preferred port', '3737')
@@ -34,10 +34,10 @@ const opts = program.opts();
34
34
  const standaloneEntry = join(packageRoot, '.next', 'standalone', 'server.js');
35
35
  if (!existsSync(standaloneEntry)) {
36
36
  console.error(`
37
- [ccdsb] Build artifact not found:
37
+ [ccgauge] Build artifact not found:
38
38
  ${standaloneEntry}
39
39
 
40
- If you installed ccdsb from npm: please reinstall — the published package should
40
+ If you installed ccgauge from npm: please reinstall — the published package should
41
41
  include the standalone build.
42
42
 
43
43
  If you are running from source: build first with
@@ -60,7 +60,7 @@ const env = {
60
60
  NODE_ENV: 'production',
61
61
  };
62
62
  if (opts.dir) {
63
- env.CCDSB_CONFIG_DIR = String(opts.dir);
63
+ env.CCGAUGE_CONFIG_DIR = String(opts.dir);
64
64
  }
65
65
 
66
66
  const child = fork(standaloneEntry, [], {
@@ -90,7 +90,7 @@ async function tryOpen() {
90
90
  waitForUrl(url, 15_000)
91
91
  .then(tryOpen)
92
92
  .catch((err) => {
93
- console.error(`\n[ccdsb] failed to start: ${err.message}\n`);
93
+ console.error(`\n[ccgauge] failed to start: ${err.message}\n`);
94
94
  child.kill('SIGTERM');
95
95
  process.exit(1);
96
96
  });
@@ -129,7 +129,7 @@ async function waitForUrl(target, timeoutMs) {
129
129
  function printReady(url) {
130
130
  const banner = [
131
131
  '',
132
- ' \x1b[1m\x1b[38;2;201;100;66mccdsb\x1b[0m Claude Code Dashboard',
132
+ ' \x1b[1m\x1b[38;2;201;100;66mccgauge\x1b[0m Claude Code Dashboard',
133
133
  '',
134
134
  ` ➜ Local: \x1b[36m${url}\x1b[0m`,
135
135
  ` ➜ Press \x1b[2mCtrl+C\x1b[0m to stop`,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ccgauge",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Claude Code Dashboard — local web UI for Claude Code token usage and cost",
5
5
  "keywords": [
6
6
  "claude",
@@ -19,7 +19,7 @@
19
19
  },
20
20
  "packageManager": "pnpm@10.30.3",
21
21
  "bin": {
22
- "ccdsb": "bin/cli.mjs"
22
+ "ccgauge": "bin/cli.mjs"
23
23
  },
24
24
  "files": [
25
25
  "bin/cli.mjs",
@@ -1,21 +0,0 @@
1
- exports.id=204,exports.ids=[204],exports.modules={3879:()=>{},22666:(a,b,c)=>{"use strict";c.r(b),c.d(b,{default:()=>n,generateMetadata:()=>l,viewport:()=>m});var d=c(80707);c(3879);var e=c(90639),f=c(49902);function g(){let a=`
2
- (function(){
3
- try {
4
- var t = null;
5
- try { t = localStorage.getItem('ccdsb.theme'); } catch (_) {}
6
- if (!t) {
7
- var m = document.cookie.match(/(?:^|; )ccdsb_theme=([^;]+)/);
8
- if (m) t = decodeURIComponent(m[1]);
9
- }
10
- if (t !== 'light' && t !== 'dark' && t !== 'system') t = 'system';
11
- var resolved = t;
12
- if (t === 'system') {
13
- resolved = window.matchMedia && window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark';
14
- }
15
- var root = document.documentElement;
16
- root.classList.remove('theme-light','theme-dark');
17
- root.classList.add(resolved === 'light' ? 'theme-light' : 'theme-dark');
18
- root.setAttribute('data-theme', resolved);
19
- } catch (e) {}
20
- })();
21
- `;return(0,d.jsx)("script",{dangerouslySetInnerHTML:{__html:a}})}var h=c(44119),i=c(65129);async function j(){try{let a=await (0,i.UL)(),b=a.get("ccdsb_theme")?.value;if("light"===b||"dark"===b||"system"===b)return b}catch{}return"system"}var k=c(28806);async function l(){let a=await (0,h.sG)();return{title:"ccdsb — Claude Code Dashboard",description:(0,k.nA)(a,"brand.tagline")}}let m={width:"device-width",initialScale:1};async function n({children:a}){let b=await (0,h.sG)(),c=await j();return(0,d.jsxs)("html",{lang:"zh"===b?"zh-CN":"en",className:"light"===c?"theme-light":"theme-dark",suppressHydrationWarning:!0,children:[(0,d.jsx)("head",{children:(0,d.jsx)(g,{})}),(0,d.jsx)("body",{className:"min-h-screen bg-bg text-text-primary",children:(0,d.jsxs)(f.Providers,{locale:b,theme:c,children:[(0,d.jsx)(e.Nav,{}),(0,d.jsx)("main",{children:a})]})})]})}},24384:(a,b,c)=>{Promise.resolve().then(c.t.bind(c,41053,23)),Promise.resolve().then(c.t.bind(c,47812,23)),Promise.resolve().then(c.t.bind(c,50954,23)),Promise.resolve().then(c.t.bind(c,12493,23)),Promise.resolve().then(c.t.bind(c,99961,23)),Promise.resolve().then(c.t.bind(c,44865,23)),Promise.resolve().then(c.t.bind(c,91307,23)),Promise.resolve().then(c.t.bind(c,89684,23)),Promise.resolve().then(c.bind(c,69977))},24713:(a,b,c)=>{"use strict";c.d(b,{ThemeSwitcher:()=>j});var d=c(11581),e=c(89864),f=c(97304),g=c(32838),h=c(57176);let i={light:"☀",dark:"☾",system:"◐"};function j(){let{theme:a,resolved:b,setTheme:c}=(0,f.D)(),{t:j}=(0,g.s9)(),[k,l]=(0,e.useState)(!1),m=(0,e.useRef)(null),n=[{value:"light",label:j("settings.theme.light")},{value:"dark",label:j("settings.theme.dark")},{value:"system",label:j("settings.theme.system")}];return(0,d.jsxs)("div",{ref:m,className:"relative",children:[(0,d.jsx)("button",{onClick:()=>l(!k),className:"btn-ghost px-2 py-1 text-base leading-none",title:`${j("theme.label")}: ${a} (${b})`,"aria-label":j("theme.label"),children:(0,d.jsx)("span",{"aria-hidden":!0,children:i[a]})}),k&&(0,d.jsx)("div",{className:"absolute right-0 mt-1 min-w-[8rem] card border-border-hi shadow-lg p-1 z-30",children:n.map(b=>(0,d.jsxs)("button",{onClick:()=>{c(b.value),l(!1)},className:(0,h.cn)("w-full text-left px-2.5 py-1.5 text-sm rounded hover:bg-bg-surface-hi flex items-center gap-2",a===b.value&&"text-text-primary"),children:[(0,d.jsx)("span",{className:"w-4 text-center","aria-hidden":!0,children:i[b.value]}),(0,d.jsx)("span",{children:b.label})]},b.value))})]})}},28806:(a,b,c)=>{"use strict";c.d(b,{Xn:()=>d,nA:()=>f});let d="en",e={en:{"brand.tagline":"claude code dashboard","nav.overview":"Overview","nav.usage":"Usage","nav.sessions":"Sessions","nav.projects":"Projects","nav.models":"Models","nav.settings":"Settings","nav.localBadge":"local","common.requests":"requests","common.tokens":"tokens","common.cost":"Cost","common.savedViaCache":"Saved {amount} via cache","common.savedTodayViaCache":"Saved {amount} today","common.live":"live","common.allModels":"All","common.allProjects":"All","common.unknown":"(unknown)","common.session":"Session","common.sessions":"sessions","common.projects":"projects","common.req":"req","common.day":"day","common.days":"days","common.activity":"activity","common.fallbackPrice":"fallback price","common.thinking":"thinking","common.lastActivity":"last activity","common.empty.title":"No usage data yet","common.empty.desc":"Open Claude Code, send a message, then refresh.","common.refresh":"Refresh","common.search":"Search…","common.searchPlaceholder":"Search model, project, session, tool…","common.exportCsv":"Export CSV","common.rows":"{count} rows","common.prev":"‹ Prev","common.next":"Next ›","common.first":"\xab","common.last":"\xbb","common.pageOf":"Page {page} of {total}","common.allSessions":"← All sessions","common.allProjectsLink":"← All projects","common.noMatchingRows":"No matching rows","range.today":"Today","range.7d":"7d","range.30d":"30d","range.90d":"90d","range.all":"All","gran.hour":"Hour","gran.day":"Day","gran.week":"Week","gran.month":"Month","overview.title":"Overview","overview.subtitle":"{count} requests across {files} files \xb7 scan in {ms}ms","overview.subtitle.empty":"Scanned {dirs} directory(ies). Open Claude Code, send a message, then refresh.","overview.empty.title":"No usage data yet","overview.kpi.tokensToday":"Tokens today","overview.kpi.costToday":"Cost today","overview.kpi.thisMonth":"This month","overview.kpi.cacheHit":"Cache hit rate","overview.kpi.topModel":"Top model","overview.kpi.activeSessions":"Sessions today","overview.kpi.activeSessions.hint":"{count} project(s)","overview.kpi.thisMonth.hint":"{tokens} tokens \xb7 {req} req","overview.kpi.tokensToday.hint":"{count} requests","overview.kpi.topModel.hint":"{pct} of cost this month","overview.kpi.cacheHit.hint":"Saved {amount} today","overview.delta.title":"vs yesterday","overview.trend.title":"Token usage trend","overview.trend.desc":"Last 30 days \xb7 stacked by token type","overview.trend.activeDays":"{n} day with activity","overview.trend.activeDays.plural":"{n} days with activity","overview.costByModel.title":"Cost by model","overview.costByModel.desc":"This month, sorted by spend","block.title":"Active 5h block","block.remaining":"remaining","block.elapsed":"{pct}% elapsed","block.tokensSuffix":"tokens","block.spentSoFar":"Spent so far","block.burnPerMin":"Burn / min","block.projectedTotal":"Projected total","block.requests":"Requests","block.empty":"No active block","block.emptyDesc":"Send a message in Claude Code to start one.","chart.legend.input":"Input","chart.legend.output":"Output","chart.legend.cacheRead":"Cache read","chart.legend.cacheWrite":"Cache write","chart.tooltip.total":"Total","chart.tooltip.cost":"Cost","chart.tooltip.requests":"Requests","chart.empty":"No data in this range","chart.empty.short":"No data","usage.title":"Usage","usage.subtitle":"{count} requests in selected range","usage.kpi.totalTokens":"Total tokens","usage.kpi.totalCost":"Total cost","usage.kpi.cacheSaved":"Cache saved","usage.kpi.cacheHit":"Cache hit","usage.trend":"Trend","usage.trend.gran":"Granularity: {gran}","usage.requests.title":"Requests","usage.requests.desc":"Detailed request log; click a header to sort","usage.col.time":"Time","usage.col.model":"Model","usage.col.project":"Project","usage.col.session":"Session","usage.col.input":"Input","usage.col.output":"Output","usage.col.cacheRead":"Cache R","usage.col.cacheWrite":"Cache W","usage.col.cost":"Cost","usage.col.tools":"Tools","filter.modelLabel":"Model","filter.projectLabel":"Project","filter.modelAll":"Model: All","filter.projectAll":"Project: All","filter.modelSingle":"Model: {value}","filter.projectSingle":"Project: {value}","filter.modelMulti":"Model: {count}","filter.projectMulti":"Project: {count}","filter.clearAll":"Clear all","filter.noOptions":"No options","sessions.title":"Sessions","sessions.subtitle":"{count} sessions \xb7 sorted by most recent activity","sessions.col.session":"Session","sessions.col.project":"Project","sessions.col.models":"Model(s)","sessions.col.requests":"Requests","sessions.col.tokens":"Tokens","sessions.col.cost":"Cost","sessions.col.duration":"Duration","sessions.col.lastActivity":"Last activity","sessions.untitled":"Session {hash}","sessions.empty":"No sessions yet","session.kpi.requests":"Requests","session.kpi.totalTokens":"Total tokens","session.kpi.cost":"Cost","session.kpi.duration":"Duration","session.timeline.title":"Message timeline","session.timeline.desc":"In order; newest at the bottom","session.perMessage.title":"Per-message tokens","session.modelsInSession":"Models in this session","session.modelLine":"{requests} req \xb7 {tokens} tokens","session.token.in":"in","session.token.out":"out","session.token.cacheR":"cache r","session.token.cacheW":"cache w","projects.title":"Projects","projects.subtitle":"{count} projects \xb7 sorted by spend","projects.empty":"No projects yet","projects.stat.sessions":"Sessions","projects.stat.requests":"Requests","projects.stat.tokens":"Tokens","project.activity":"Activity (last 30 days)","project.sessions.title":"Sessions ({count})","models.title":"Models","models.subtitle":"{count} model(s) used in total","models.empty":"No model usage yet","models.share.cost":"Cost share","models.share.tokens":"Tokens share","models.share.cacheHit":"Cache hit","models.field.requests":"Requests","models.field.savedByCache":"Saved by cache","models.field.input1M":"Input / 1M","models.field.output1M":"Output / 1M","models.field.cacheRead1M":"Cache read / 1M","models.field.pctOfTotal":"{pct} of total spend \xb7 {tokens} tokens","models.eachTrend":"Combined trend (last 30 days)","settings.title":"Settings","settings.subtitle":"Data sources, pricing, and behavior","settings.dataSources.title":"Data sources","settings.dataSources.desc":"ccdsb scans these locations for JSONL files","settings.dataSources.active":"active","settings.dataSources.notPresent":"not present","settings.dataSources.envHint":"Override with {env1} or {env2} environment variables (the dashboard appends {appendix}).","settings.rescan":"Rescan now","settings.rescanning":"Rescanning…","settings.scanStats.title":"Scan stats","settings.scanStats.files":"Files scanned","settings.scanStats.records":"Records parsed","settings.scanStats.assistant":"Assistant records (deduped)","settings.scanStats.duration":"Scan duration","settings.pricing.title":"Pricing table","settings.pricing.desc":"USD per 1M tokens \xb7 built-in snapshot, fuzzy match for date-suffixed model names","settings.pricing.col.model":"Model","settings.pricing.col.input":"Input","settings.pricing.col.output":"Output","settings.pricing.col.write5m":"Cache write 5m","settings.pricing.col.write1h":"Cache write 1h","settings.pricing.col.read":"Cache read","settings.preferences.title":"Preferences","settings.preferences.language":"Language","settings.preferences.theme":"Theme","settings.theme.light":"Light","settings.theme.dark":"Dark","settings.theme.system":"System","settings.about.title":"About","settings.about.subtitle":"Version {version} \xb7 MIT licensed","settings.about.line1":"Fully local: data never leaves your machine; no telemetry, no network calls.","settings.about.line2":"Read-only: ccdsb only reads JSONL files, never writes back to ~/.claude.","settings.about.line3":'Cache: scan results are memoized for 5s; click "Rescan" to force a fresh read.',"settings.about.line4":"Stop with Ctrl+C in the terminal that started ccdsb.","lang.label":"Language","lang.en":"English","lang.zh":"中文","theme.label":"Theme"},zh:{"brand.tagline":"Claude Code 用量看板","nav.overview":"概览","nav.usage":"用量","nav.sessions":"会话","nav.projects":"项目","nav.models":"模型","nav.settings":"设置","nav.localBadge":"本地","common.requests":"次请求","common.tokens":"tokens","common.cost":"花费","common.savedViaCache":"通过缓存节省 {amount}","common.savedTodayViaCache":"今日缓存节省 {amount}","common.live":"进行中","common.allModels":"全部","common.allProjects":"全部","common.unknown":"(未知)","common.session":"会话","common.sessions":"个会话","common.projects":"个项目","common.req":"请求","common.day":"天","common.days":"天","common.activity":"活跃","common.fallbackPrice":"使用兜底单价","common.thinking":"思考","common.lastActivity":"最近活跃","common.empty.title":"暂无用量数据","common.empty.desc":"打开 Claude Code 发送一条消息后刷新本页。","common.refresh":"刷新","common.search":"搜索…","common.searchPlaceholder":"搜索模型 / 项目 / 会话 / 工具…","common.exportCsv":"导出 CSV","common.rows":"{count} 行","common.prev":"‹ 上一页","common.next":"下一页 ›","common.first":"\xab","common.last":"\xbb","common.pageOf":"第 {page} / {total} 页","common.allSessions":"← 返回会话列表","common.allProjectsLink":"← 返回项目列表","common.noMatchingRows":"没有匹配的记录","range.today":"今天","range.7d":"7 天","range.30d":"30 天","range.90d":"90 天","range.all":"全部","gran.hour":"小时","gran.day":"天","gran.week":"周","gran.month":"月","overview.title":"概览","overview.subtitle":"{count} 次请求,覆盖 {files} 个文件 \xb7 扫描耗时 {ms}ms","overview.subtitle.empty":"已扫描 {dirs} 个目录。打开 Claude Code 发一条消息后刷新本页。","overview.empty.title":"暂无用量数据","overview.kpi.tokensToday":"今日 tokens","overview.kpi.costToday":"今日花费","overview.kpi.thisMonth":"本月累计","overview.kpi.cacheHit":"缓存命中率","overview.kpi.topModel":"主力模型","overview.kpi.activeSessions":"今日会话","overview.kpi.activeSessions.hint":"涉及 {count} 个项目","overview.kpi.thisMonth.hint":"{tokens} tokens \xb7 {req} 次请求","overview.kpi.tokensToday.hint":"{count} 次请求","overview.kpi.topModel.hint":"本月成本占比 {pct}","overview.kpi.cacheHit.hint":"今日缓存节省 {amount}","overview.delta.title":"相比昨日","overview.trend.title":"Token 用量趋势","overview.trend.desc":"近 30 天 \xb7 按 token 类型堆叠","overview.trend.activeDays":"{n} 天有数据","overview.trend.activeDays.plural":"{n} 天有数据","overview.costByModel.title":"按模型成本分布","overview.costByModel.desc":"本月,按花费排序","block.title":"当前 5h block","block.remaining":"剩余","block.elapsed":"已用 {pct}%","block.tokensSuffix":"tokens","block.spentSoFar":"已花费","block.burnPerMin":"每分钟消耗","block.projectedTotal":"预计总花费","block.requests":"请求数","block.empty":"当前无活跃 block","block.emptyDesc":"在 Claude Code 中发送消息会启动一个新 block。","chart.legend.input":"输入","chart.legend.output":"输出","chart.legend.cacheRead":"缓存读取","chart.legend.cacheWrite":"缓存写入","chart.tooltip.total":"合计","chart.tooltip.cost":"花费","chart.tooltip.requests":"请求数","chart.empty":"区间内无数据","chart.empty.short":"暂无数据","usage.title":"用量明细","usage.subtitle":"当前筛选范围内 {count} 次请求","usage.kpi.totalTokens":"总 tokens","usage.kpi.totalCost":"总花费","usage.kpi.cacheSaved":"缓存节省","usage.kpi.cacheHit":"缓存命中","usage.trend":"趋势","usage.trend.gran":"粒度:{gran}","usage.requests.title":"请求列表","usage.requests.desc":"逐条请求记录;点击表头排序","usage.col.time":"时间","usage.col.model":"模型","usage.col.project":"项目","usage.col.session":"会话","usage.col.input":"输入","usage.col.output":"输出","usage.col.cacheRead":"缓存读","usage.col.cacheWrite":"缓存写","usage.col.cost":"花费","usage.col.tools":"工具","filter.modelLabel":"模型","filter.projectLabel":"项目","filter.modelAll":"模型:全部","filter.projectAll":"项目:全部","filter.modelSingle":"模型:{value}","filter.projectSingle":"项目:{value}","filter.modelMulti":"模型:{count} 个","filter.projectMulti":"项目:{count} 个","filter.clearAll":"清除筛选","filter.noOptions":"没有可选项","sessions.title":"会话","sessions.subtitle":"共 {count} 个会话 \xb7 按最近活跃排序","sessions.col.session":"会话","sessions.col.project":"项目","sessions.col.models":"模型","sessions.col.requests":"请求数","sessions.col.tokens":"Tokens","sessions.col.cost":"花费","sessions.col.duration":"时长","sessions.col.lastActivity":"最近活跃","sessions.untitled":"会话 {hash}","sessions.empty":"暂无会话","session.kpi.requests":"请求数","session.kpi.totalTokens":"总 tokens","session.kpi.cost":"花费","session.kpi.duration":"时长","session.timeline.title":"消息时间线","session.timeline.desc":"按时间正序,最新的在最下方","session.perMessage.title":"逐条消息 tokens","session.modelsInSession":"本会话使用的模型","session.modelLine":"{requests} 次 \xb7 {tokens} tokens","session.token.in":"输入","session.token.out":"输出","session.token.cacheR":"缓存读","session.token.cacheW":"缓存写","projects.title":"项目","projects.subtitle":"共 {count} 个项目 \xb7 按花费排序","projects.empty":"暂无项目","projects.stat.sessions":"会话","projects.stat.requests":"请求","projects.stat.tokens":"Tokens","project.activity":"活跃情况(近 30 天)","project.sessions.title":"会话({count})","models.title":"模型","models.subtitle":"共使用过 {count} 个模型","models.empty":"暂无模型用量","models.share.cost":"成本占比","models.share.tokens":"Tokens 占比","models.share.cacheHit":"缓存命中","models.field.requests":"请求数","models.field.savedByCache":"缓存节省","models.field.input1M":"输入 / 1M","models.field.output1M":"输出 / 1M","models.field.cacheRead1M":"缓存读 / 1M","models.field.pctOfTotal":"占总花费 {pct} \xb7 {tokens} tokens","models.eachTrend":"整体趋势(近 30 天)","settings.title":"设置","settings.subtitle":"数据源、价格表与行为偏好","settings.dataSources.title":"数据源","settings.dataSources.desc":"ccdsb 会扫描以下路径的 JSONL 文件","settings.dataSources.active":"已启用","settings.dataSources.notPresent":"不存在","settings.dataSources.envHint":"可以通过环境变量 {env1} 或 {env2} 自定义路径(看板会自动追加 {appendix})。","settings.rescan":"立即重新扫描","settings.rescanning":"扫描中…","settings.scanStats.title":"扫描统计","settings.scanStats.files":"扫描文件数","settings.scanStats.records":"解析记录数","settings.scanStats.assistant":"去重后 assistant 记录","settings.scanStats.duration":"扫描耗时","settings.pricing.title":"价格表","settings.pricing.desc":"美元 / 1M tokens \xb7 内置快照,对带日期后缀的模型名做 fuzzy 匹配","settings.pricing.col.model":"模型","settings.pricing.col.input":"输入","settings.pricing.col.output":"输出","settings.pricing.col.write5m":"缓存写入 5 分钟","settings.pricing.col.write1h":"缓存写入 1 小时","settings.pricing.col.read":"缓存读取","settings.preferences.title":"偏好设置","settings.preferences.language":"语言","settings.preferences.theme":"主题","settings.theme.light":"亮色","settings.theme.dark":"暗色","settings.theme.system":"跟随系统","settings.about.title":"关于","settings.about.subtitle":"版本 {version} \xb7 MIT 协议","settings.about.line1":"完全本地:数据从不离开你的机器,没有任何遥测、没有任何网络调用。","settings.about.line2":"只读:ccdsb 只读取 JSONL,绝不会写回 ~/.claude。","settings.about.line3":'缓存:扫描结果会缓存 5 秒;点击"重新扫描"可强制刷新。',"settings.about.line4":"在启动 ccdsb 的终端按 Ctrl+C 即可停止。","lang.label":"语言","lang.en":"English","lang.zh":"中文","theme.label":"主题"}};function f(a,b,c){let d=e[a]?.[b];if(void 0===d&&(d=e.en[b]),void 0===d&&(d=b),c)for(let[a,b]of Object.entries(c))d=d.replace(RegExp(`\\{${a}\\}`,"g"),String(b));return d}},31236:(a,b,c)=>{"use strict";c.d(b,{LanguageSwitcher:()=>k});var d=c(11581),e=c(89864),f=c(32838),g=c(75688),h=c(57176);let i={en:"EN",zh:"中"},j={en:"English",zh:"中文"};function k(){let{locale:a,setLocale:b,t:c}=(0,f.s9)(),[k,l]=(0,e.useState)(!1),m=(0,e.useRef)(null);return(0,d.jsxs)("div",{ref:m,className:"relative",children:[(0,d.jsx)("button",{onClick:()=>l(!k),className:"btn-ghost px-2 py-1 text-xs font-medium",title:c("lang.label"),"aria-label":c("lang.label"),children:i[a]}),k&&(0,d.jsx)("div",{className:"absolute right-0 mt-1 min-w-[8rem] card border-border-hi shadow-lg p-1 z-30",children:g.YZ.map(c=>(0,d.jsxs)("button",{onClick:()=>{b(c),l(!1)},className:(0,h.cn)("w-full text-left px-2.5 py-1.5 text-sm rounded hover:bg-bg-surface-hi flex items-center gap-2",a===c&&"text-text-primary"),children:[(0,d.jsx)("span",{className:(0,h.cn)("w-3.5 h-3.5 rounded-sm border flex items-center justify-center text-[10px]",a===c?"bg-brand border-brand text-white":"border-border-hi"),children:a===c?"✓":""}),(0,d.jsx)("span",{children:j[c]})]},c))})]})}},32838:(a,b,c)=>{"use strict";c.d(b,{CY:()=>i,s9:()=>j,kj:()=>k});var d=c(11581),e=c(89864),f=c(33479),g=c(75688);let h=(0,e.createContext)({locale:g.Xn,setLocale:()=>{},t:a=>a});function i({initialLocale:a,children:b}){let c=(0,f.useRouter)(),i=(0,e.useCallback)(a=>{try{localStorage.setItem("ccdsb.locale",a)}catch{}document.cookie=`ccdsb_locale=${a}; path=/; max-age=31536000; SameSite=Lax`,c.refresh()},[c]),j=(0,e.useCallback)((b,c)=>(0,g.nA)(a,b,c),[a]),k=(0,e.useMemo)(()=>({locale:a,setLocale:i,t:j}),[a,i,j]);return(0,d.jsx)(h.Provider,{value:k,children:b})}function j(){return(0,e.useContext)(h)}function k(){return(0,e.useContext)(h).t}},36243:(a,b,c)=>{Promise.resolve().then(c.bind(c,97273)),Promise.resolve().then(c.bind(c,66548))},44119:(a,b,c)=>{"use strict";c.d(b,{sG:()=>f,yO:()=>g});var d=c(65129),e=c(28806);async function f(){try{let a=await (0,d.UL)(),b=a.get("ccdsb_locale")?.value;if("zh"===b||"en"===b)return b}catch{}return e.Xn}async function g(){let a=await f();return(b,c)=>(0,e.nA)(a,b,c)}},49902:(a,b,c)=>{"use strict";c.d(b,{Providers:()=>d});let d=(0,c(58979).registerClientReference)(function(){throw Error("Attempted to call Providers() from the server but Providers is on the client. It's not possible to invoke a client function from the server, it can only be rendered as a Component or passed to props of a Client Component.")},"/Users/zuopeng.cheng/personal/workspace/ccdsb/components/providers.tsx","Providers")},57176:(a,b,c)=>{"use strict";c.d(b,{BC:()=>n,P6:()=>o,PJ:()=>m,R8:()=>j,az:()=>h,cn:()=>f,jh:()=>g,l7:()=>i,om:()=>k,r6:()=>l});var d=c(45301),e=c(86981);function f(...a){return(0,e.QP)((0,d.$)(a))}function g(a){return Number.isFinite(a)?a>=1e9?(a/1e9).toFixed(2)+"B":a>=1e6?(a/1e6).toFixed(2)+"M":a>=1e3?(a/1e3).toFixed(1)+"K":new Intl.NumberFormat("en-US",{maximumFractionDigits:(void 0)??0}).format(a):"0"}function h(a,b){return new Intl.NumberFormat("en-US",{style:"currency",currency:"USD",minimumFractionDigits:b?.minFrac??2,maximumFractionDigits:b?.maxFrac??2}).format(a)}function i(a){return 0===a?"$0":a<.01?new Intl.NumberFormat("en-US",{style:"currency",currency:"USD",minimumFractionDigits:4,maximumFractionDigits:6}).format(a):h(a)}function j(a,b=1){return Number.isFinite(a)?`${(100*a).toFixed(b)}%`:"0%"}function k(a){let b="string"==typeof a||"number"==typeof a?new Date(a):a,c=Date.now()-b.getTime();if(c<0)return"just now";let d=Math.floor(c/1e3);if(d<60)return`${d}s ago`;let e=Math.floor(d/60);if(e<60)return`${e}m ago`;let f=Math.floor(e/60);if(f<24)return`${f}h ago`;let g=Math.floor(f/24);if(g<7)return`${g}d ago`;let h=Math.floor(g/7);if(h<5)return`${h}w ago`;let i=Math.floor(g/30);return i<12?`${i}mo ago`:`${Math.floor(g/365)}y ago`}function l(a){let b="string"==typeof a||"number"==typeof a?new Date(a):a;if(Number.isNaN(b.getTime()))return"";let c=b.getFullYear(),d=String(b.getMonth()+1).padStart(2,"0"),e=String(b.getDate()).padStart(2,"0"),f=String(b.getHours()).padStart(2,"0"),g=String(b.getMinutes()).padStart(2,"0");return`${c}-${d}-${e} ${f}:${g}`}function m(a){if(!a)return"(unknown)";let b=a.replace(/\/+$/,"").split("/");return b[b.length-1]||a}function n(a,b=8){return a?a.replace(/-/g,"").slice(0,b):""}function o(a){if(!a)return"(unknown)";let b=a.replace(/-(\d{8})$/,"").replace(/^(vertex_ai|bedrock|anthropic)\//,""),c=(b=b.replace(/^claude-/,"")).split("-");if(c.length>=2){let a=c[0],b=c.slice(1).join(".");return p(a)+" "+b}return p(b.replace(/-/g," "))}function p(a){return a.replace(/\b\w/g,a=>a.toUpperCase())}},60832:(a,b,c)=>{Promise.resolve().then(c.t.bind(c,57535,23)),Promise.resolve().then(c.t.bind(c,97262,23)),Promise.resolve().then(c.t.bind(c,70184,23)),Promise.resolve().then(c.t.bind(c,66623,23)),Promise.resolve().then(c.t.bind(c,76983,23)),Promise.resolve().then(c.t.bind(c,43199,23)),Promise.resolve().then(c.t.bind(c,32313,23)),Promise.resolve().then(c.t.bind(c,43462,23)),Promise.resolve().then(c.t.bind(c,60367,23))},66548:(a,b,c)=>{"use strict";c.d(b,{Providers:()=>g});var d=c(11581),e=c(32838),f=c(97304);function g({locale:a,theme:b,children:c}){return(0,d.jsx)(e.CY,{initialLocale:a,children:(0,d.jsx)(f.N,{initialTheme:b,children:c})})}},75688:(a,b,c)=>{"use strict";c.d(b,{Xn:()=>e,YZ:()=>d,nA:()=>g});let d=["en","zh"],e="en",f={en:{"brand.tagline":"claude code dashboard","nav.overview":"Overview","nav.usage":"Usage","nav.sessions":"Sessions","nav.projects":"Projects","nav.models":"Models","nav.settings":"Settings","nav.localBadge":"local","common.requests":"requests","common.tokens":"tokens","common.cost":"Cost","common.savedViaCache":"Saved {amount} via cache","common.savedTodayViaCache":"Saved {amount} today","common.live":"live","common.allModels":"All","common.allProjects":"All","common.unknown":"(unknown)","common.session":"Session","common.sessions":"sessions","common.projects":"projects","common.req":"req","common.day":"day","common.days":"days","common.activity":"activity","common.fallbackPrice":"fallback price","common.thinking":"thinking","common.lastActivity":"last activity","common.empty.title":"No usage data yet","common.empty.desc":"Open Claude Code, send a message, then refresh.","common.refresh":"Refresh","common.search":"Search…","common.searchPlaceholder":"Search model, project, session, tool…","common.exportCsv":"Export CSV","common.rows":"{count} rows","common.prev":"‹ Prev","common.next":"Next ›","common.first":"\xab","common.last":"\xbb","common.pageOf":"Page {page} of {total}","common.allSessions":"← All sessions","common.allProjectsLink":"← All projects","common.noMatchingRows":"No matching rows","range.today":"Today","range.7d":"7d","range.30d":"30d","range.90d":"90d","range.all":"All","gran.hour":"Hour","gran.day":"Day","gran.week":"Week","gran.month":"Month","overview.title":"Overview","overview.subtitle":"{count} requests across {files} files \xb7 scan in {ms}ms","overview.subtitle.empty":"Scanned {dirs} directory(ies). Open Claude Code, send a message, then refresh.","overview.empty.title":"No usage data yet","overview.kpi.tokensToday":"Tokens today","overview.kpi.costToday":"Cost today","overview.kpi.thisMonth":"This month","overview.kpi.cacheHit":"Cache hit rate","overview.kpi.topModel":"Top model","overview.kpi.activeSessions":"Sessions today","overview.kpi.activeSessions.hint":"{count} project(s)","overview.kpi.thisMonth.hint":"{tokens} tokens \xb7 {req} req","overview.kpi.tokensToday.hint":"{count} requests","overview.kpi.topModel.hint":"{pct} of cost this month","overview.kpi.cacheHit.hint":"Saved {amount} today","overview.delta.title":"vs yesterday","overview.trend.title":"Token usage trend","overview.trend.desc":"Last 30 days \xb7 stacked by token type","overview.trend.activeDays":"{n} day with activity","overview.trend.activeDays.plural":"{n} days with activity","overview.costByModel.title":"Cost by model","overview.costByModel.desc":"This month, sorted by spend","block.title":"Active 5h block","block.remaining":"remaining","block.elapsed":"{pct}% elapsed","block.tokensSuffix":"tokens","block.spentSoFar":"Spent so far","block.burnPerMin":"Burn / min","block.projectedTotal":"Projected total","block.requests":"Requests","block.empty":"No active block","block.emptyDesc":"Send a message in Claude Code to start one.","chart.legend.input":"Input","chart.legend.output":"Output","chart.legend.cacheRead":"Cache read","chart.legend.cacheWrite":"Cache write","chart.tooltip.total":"Total","chart.tooltip.cost":"Cost","chart.tooltip.requests":"Requests","chart.empty":"No data in this range","chart.empty.short":"No data","usage.title":"Usage","usage.subtitle":"{count} requests in selected range","usage.kpi.totalTokens":"Total tokens","usage.kpi.totalCost":"Total cost","usage.kpi.cacheSaved":"Cache saved","usage.kpi.cacheHit":"Cache hit","usage.trend":"Trend","usage.trend.gran":"Granularity: {gran}","usage.requests.title":"Requests","usage.requests.desc":"Detailed request log; click a header to sort","usage.col.time":"Time","usage.col.model":"Model","usage.col.project":"Project","usage.col.session":"Session","usage.col.input":"Input","usage.col.output":"Output","usage.col.cacheRead":"Cache R","usage.col.cacheWrite":"Cache W","usage.col.cost":"Cost","usage.col.tools":"Tools","filter.modelLabel":"Model","filter.projectLabel":"Project","filter.modelAll":"Model: All","filter.projectAll":"Project: All","filter.modelSingle":"Model: {value}","filter.projectSingle":"Project: {value}","filter.modelMulti":"Model: {count}","filter.projectMulti":"Project: {count}","filter.clearAll":"Clear all","filter.noOptions":"No options","sessions.title":"Sessions","sessions.subtitle":"{count} sessions \xb7 sorted by most recent activity","sessions.col.session":"Session","sessions.col.project":"Project","sessions.col.models":"Model(s)","sessions.col.requests":"Requests","sessions.col.tokens":"Tokens","sessions.col.cost":"Cost","sessions.col.duration":"Duration","sessions.col.lastActivity":"Last activity","sessions.untitled":"Session {hash}","sessions.empty":"No sessions yet","session.kpi.requests":"Requests","session.kpi.totalTokens":"Total tokens","session.kpi.cost":"Cost","session.kpi.duration":"Duration","session.timeline.title":"Message timeline","session.timeline.desc":"In order; newest at the bottom","session.perMessage.title":"Per-message tokens","session.modelsInSession":"Models in this session","session.modelLine":"{requests} req \xb7 {tokens} tokens","session.token.in":"in","session.token.out":"out","session.token.cacheR":"cache r","session.token.cacheW":"cache w","projects.title":"Projects","projects.subtitle":"{count} projects \xb7 sorted by spend","projects.empty":"No projects yet","projects.stat.sessions":"Sessions","projects.stat.requests":"Requests","projects.stat.tokens":"Tokens","project.activity":"Activity (last 30 days)","project.sessions.title":"Sessions ({count})","models.title":"Models","models.subtitle":"{count} model(s) used in total","models.empty":"No model usage yet","models.share.cost":"Cost share","models.share.tokens":"Tokens share","models.share.cacheHit":"Cache hit","models.field.requests":"Requests","models.field.savedByCache":"Saved by cache","models.field.input1M":"Input / 1M","models.field.output1M":"Output / 1M","models.field.cacheRead1M":"Cache read / 1M","models.field.pctOfTotal":"{pct} of total spend \xb7 {tokens} tokens","models.eachTrend":"Combined trend (last 30 days)","settings.title":"Settings","settings.subtitle":"Data sources, pricing, and behavior","settings.dataSources.title":"Data sources","settings.dataSources.desc":"ccdsb scans these locations for JSONL files","settings.dataSources.active":"active","settings.dataSources.notPresent":"not present","settings.dataSources.envHint":"Override with {env1} or {env2} environment variables (the dashboard appends {appendix}).","settings.rescan":"Rescan now","settings.rescanning":"Rescanning…","settings.scanStats.title":"Scan stats","settings.scanStats.files":"Files scanned","settings.scanStats.records":"Records parsed","settings.scanStats.assistant":"Assistant records (deduped)","settings.scanStats.duration":"Scan duration","settings.pricing.title":"Pricing table","settings.pricing.desc":"USD per 1M tokens \xb7 built-in snapshot, fuzzy match for date-suffixed model names","settings.pricing.col.model":"Model","settings.pricing.col.input":"Input","settings.pricing.col.output":"Output","settings.pricing.col.write5m":"Cache write 5m","settings.pricing.col.write1h":"Cache write 1h","settings.pricing.col.read":"Cache read","settings.preferences.title":"Preferences","settings.preferences.language":"Language","settings.preferences.theme":"Theme","settings.theme.light":"Light","settings.theme.dark":"Dark","settings.theme.system":"System","settings.about.title":"About","settings.about.subtitle":"Version {version} \xb7 MIT licensed","settings.about.line1":"Fully local: data never leaves your machine; no telemetry, no network calls.","settings.about.line2":"Read-only: ccdsb only reads JSONL files, never writes back to ~/.claude.","settings.about.line3":'Cache: scan results are memoized for 5s; click "Rescan" to force a fresh read.',"settings.about.line4":"Stop with Ctrl+C in the terminal that started ccdsb.","lang.label":"Language","lang.en":"English","lang.zh":"中文","theme.label":"Theme"},zh:{"brand.tagline":"Claude Code 用量看板","nav.overview":"概览","nav.usage":"用量","nav.sessions":"会话","nav.projects":"项目","nav.models":"模型","nav.settings":"设置","nav.localBadge":"本地","common.requests":"次请求","common.tokens":"tokens","common.cost":"花费","common.savedViaCache":"通过缓存节省 {amount}","common.savedTodayViaCache":"今日缓存节省 {amount}","common.live":"进行中","common.allModels":"全部","common.allProjects":"全部","common.unknown":"(未知)","common.session":"会话","common.sessions":"个会话","common.projects":"个项目","common.req":"请求","common.day":"天","common.days":"天","common.activity":"活跃","common.fallbackPrice":"使用兜底单价","common.thinking":"思考","common.lastActivity":"最近活跃","common.empty.title":"暂无用量数据","common.empty.desc":"打开 Claude Code 发送一条消息后刷新本页。","common.refresh":"刷新","common.search":"搜索…","common.searchPlaceholder":"搜索模型 / 项目 / 会话 / 工具…","common.exportCsv":"导出 CSV","common.rows":"{count} 行","common.prev":"‹ 上一页","common.next":"下一页 ›","common.first":"\xab","common.last":"\xbb","common.pageOf":"第 {page} / {total} 页","common.allSessions":"← 返回会话列表","common.allProjectsLink":"← 返回项目列表","common.noMatchingRows":"没有匹配的记录","range.today":"今天","range.7d":"7 天","range.30d":"30 天","range.90d":"90 天","range.all":"全部","gran.hour":"小时","gran.day":"天","gran.week":"周","gran.month":"月","overview.title":"概览","overview.subtitle":"{count} 次请求,覆盖 {files} 个文件 \xb7 扫描耗时 {ms}ms","overview.subtitle.empty":"已扫描 {dirs} 个目录。打开 Claude Code 发一条消息后刷新本页。","overview.empty.title":"暂无用量数据","overview.kpi.tokensToday":"今日 tokens","overview.kpi.costToday":"今日花费","overview.kpi.thisMonth":"本月累计","overview.kpi.cacheHit":"缓存命中率","overview.kpi.topModel":"主力模型","overview.kpi.activeSessions":"今日会话","overview.kpi.activeSessions.hint":"涉及 {count} 个项目","overview.kpi.thisMonth.hint":"{tokens} tokens \xb7 {req} 次请求","overview.kpi.tokensToday.hint":"{count} 次请求","overview.kpi.topModel.hint":"本月成本占比 {pct}","overview.kpi.cacheHit.hint":"今日缓存节省 {amount}","overview.delta.title":"相比昨日","overview.trend.title":"Token 用量趋势","overview.trend.desc":"近 30 天 \xb7 按 token 类型堆叠","overview.trend.activeDays":"{n} 天有数据","overview.trend.activeDays.plural":"{n} 天有数据","overview.costByModel.title":"按模型成本分布","overview.costByModel.desc":"本月,按花费排序","block.title":"当前 5h block","block.remaining":"剩余","block.elapsed":"已用 {pct}%","block.tokensSuffix":"tokens","block.spentSoFar":"已花费","block.burnPerMin":"每分钟消耗","block.projectedTotal":"预计总花费","block.requests":"请求数","block.empty":"当前无活跃 block","block.emptyDesc":"在 Claude Code 中发送消息会启动一个新 block。","chart.legend.input":"输入","chart.legend.output":"输出","chart.legend.cacheRead":"缓存读取","chart.legend.cacheWrite":"缓存写入","chart.tooltip.total":"合计","chart.tooltip.cost":"花费","chart.tooltip.requests":"请求数","chart.empty":"区间内无数据","chart.empty.short":"暂无数据","usage.title":"用量明细","usage.subtitle":"当前筛选范围内 {count} 次请求","usage.kpi.totalTokens":"总 tokens","usage.kpi.totalCost":"总花费","usage.kpi.cacheSaved":"缓存节省","usage.kpi.cacheHit":"缓存命中","usage.trend":"趋势","usage.trend.gran":"粒度:{gran}","usage.requests.title":"请求列表","usage.requests.desc":"逐条请求记录;点击表头排序","usage.col.time":"时间","usage.col.model":"模型","usage.col.project":"项目","usage.col.session":"会话","usage.col.input":"输入","usage.col.output":"输出","usage.col.cacheRead":"缓存读","usage.col.cacheWrite":"缓存写","usage.col.cost":"花费","usage.col.tools":"工具","filter.modelLabel":"模型","filter.projectLabel":"项目","filter.modelAll":"模型:全部","filter.projectAll":"项目:全部","filter.modelSingle":"模型:{value}","filter.projectSingle":"项目:{value}","filter.modelMulti":"模型:{count} 个","filter.projectMulti":"项目:{count} 个","filter.clearAll":"清除筛选","filter.noOptions":"没有可选项","sessions.title":"会话","sessions.subtitle":"共 {count} 个会话 \xb7 按最近活跃排序","sessions.col.session":"会话","sessions.col.project":"项目","sessions.col.models":"模型","sessions.col.requests":"请求数","sessions.col.tokens":"Tokens","sessions.col.cost":"花费","sessions.col.duration":"时长","sessions.col.lastActivity":"最近活跃","sessions.untitled":"会话 {hash}","sessions.empty":"暂无会话","session.kpi.requests":"请求数","session.kpi.totalTokens":"总 tokens","session.kpi.cost":"花费","session.kpi.duration":"时长","session.timeline.title":"消息时间线","session.timeline.desc":"按时间正序,最新的在最下方","session.perMessage.title":"逐条消息 tokens","session.modelsInSession":"本会话使用的模型","session.modelLine":"{requests} 次 \xb7 {tokens} tokens","session.token.in":"输入","session.token.out":"输出","session.token.cacheR":"缓存读","session.token.cacheW":"缓存写","projects.title":"项目","projects.subtitle":"共 {count} 个项目 \xb7 按花费排序","projects.empty":"暂无项目","projects.stat.sessions":"会话","projects.stat.requests":"请求","projects.stat.tokens":"Tokens","project.activity":"活跃情况(近 30 天)","project.sessions.title":"会话({count})","models.title":"模型","models.subtitle":"共使用过 {count} 个模型","models.empty":"暂无模型用量","models.share.cost":"成本占比","models.share.tokens":"Tokens 占比","models.share.cacheHit":"缓存命中","models.field.requests":"请求数","models.field.savedByCache":"缓存节省","models.field.input1M":"输入 / 1M","models.field.output1M":"输出 / 1M","models.field.cacheRead1M":"缓存读 / 1M","models.field.pctOfTotal":"占总花费 {pct} \xb7 {tokens} tokens","models.eachTrend":"整体趋势(近 30 天)","settings.title":"设置","settings.subtitle":"数据源、价格表与行为偏好","settings.dataSources.title":"数据源","settings.dataSources.desc":"ccdsb 会扫描以下路径的 JSONL 文件","settings.dataSources.active":"已启用","settings.dataSources.notPresent":"不存在","settings.dataSources.envHint":"可以通过环境变量 {env1} 或 {env2} 自定义路径(看板会自动追加 {appendix})。","settings.rescan":"立即重新扫描","settings.rescanning":"扫描中…","settings.scanStats.title":"扫描统计","settings.scanStats.files":"扫描文件数","settings.scanStats.records":"解析记录数","settings.scanStats.assistant":"去重后 assistant 记录","settings.scanStats.duration":"扫描耗时","settings.pricing.title":"价格表","settings.pricing.desc":"美元 / 1M tokens \xb7 内置快照,对带日期后缀的模型名做 fuzzy 匹配","settings.pricing.col.model":"模型","settings.pricing.col.input":"输入","settings.pricing.col.output":"输出","settings.pricing.col.write5m":"缓存写入 5 分钟","settings.pricing.col.write1h":"缓存写入 1 小时","settings.pricing.col.read":"缓存读取","settings.preferences.title":"偏好设置","settings.preferences.language":"语言","settings.preferences.theme":"主题","settings.theme.light":"亮色","settings.theme.dark":"暗色","settings.theme.system":"跟随系统","settings.about.title":"关于","settings.about.subtitle":"版本 {version} \xb7 MIT 协议","settings.about.line1":"完全本地:数据从不离开你的机器,没有任何遥测、没有任何网络调用。","settings.about.line2":"只读:ccdsb 只读取 JSONL,绝不会写回 ~/.claude。","settings.about.line3":'缓存:扫描结果会缓存 5 秒;点击"重新扫描"可强制刷新。',"settings.about.line4":"在启动 ccdsb 的终端按 Ctrl+C 即可停止。","lang.label":"语言","lang.en":"English","lang.zh":"中文","theme.label":"主题"}};function g(a,b,c){let d=f[a]?.[b];if(void 0===d&&(d=f.en[b]),void 0===d&&(d=b),c)for(let[a,b]of Object.entries(c))d=d.replace(RegExp(`\\{${a}\\}`,"g"),String(b));return d}},90639:(a,b,c)=>{"use strict";c.d(b,{Nav:()=>d});let d=(0,c(58979).registerClientReference)(function(){throw Error("Attempted to call Nav() from the server but Nav is on the client. It's not possible to invoke a client function from the server, it can only be rendered as a Component or passed to props of a Client Component.")},"/Users/zuopeng.cheng/personal/workspace/ccdsb/components/nav.tsx","Nav")},97273:(a,b,c)=>{"use strict";c.d(b,{Nav:()=>m});var d=c(11581),e=c(3024),f=c.n(e),g=c(33479),h=c(57176),i=c(32838),j=c(31236),k=c(24713);let l=[{href:"/",tk:"nav.overview",exact:!0},{href:"/usage",tk:"nav.usage"},{href:"/sessions",tk:"nav.sessions"},{href:"/projects",tk:"nav.projects"},{href:"/models",tk:"nav.models"},{href:"/settings",tk:"nav.settings"}];function m(){let a=(0,g.usePathname)(),b=(0,i.kj)();return(0,d.jsx)("header",{className:"sticky top-0 z-30 border-b border-border bg-bg-base/80 backdrop-blur",children:(0,d.jsxs)("div",{className:"max-w-7xl mx-auto px-6 h-14 flex items-center gap-4",children:[(0,d.jsxs)(f(),{href:"/",className:"flex items-center gap-2 font-semibold tracking-tight whitespace-nowrap",children:[(0,d.jsx)("span",{className:"inline-flex items-center justify-center w-7 h-7 rounded-button bg-brand text-white text-xs font-bold",children:"cc"}),(0,d.jsx)("span",{children:"ccdsb"}),(0,d.jsx)("span",{className:"text-xs text-text-tertiary font-normal hidden md:inline",children:b("brand.tagline")})]}),(0,d.jsx)("nav",{className:"flex-1 flex items-center gap-1",children:l.map(c=>{let e=c.exact?a===c.href:a===c.href||a.startsWith(c.href+"/");return(0,d.jsx)(f(),{href:c.href,className:(0,h.cn)("px-3 py-1.5 text-sm rounded-button font-medium transition-colors",e?"text-text-primary bg-bg-surface-hi":"text-text-secondary hover:text-text-primary hover:bg-bg-surface"),children:b(c.tk)},c.href)})}),(0,d.jsxs)("div",{className:"flex items-center gap-1",children:[(0,d.jsx)("span",{className:"pill bg-bg-surface-hi text-text-tertiary text-[10px] uppercase tracking-wide",children:b("nav.localBadge")}),(0,d.jsx)(j.LanguageSwitcher,{}),(0,d.jsx)(k.ThemeSwitcher,{})]})]})})}},97304:(a,b,c)=>{"use strict";c.d(b,{N:()=>h,D:()=>i});var d=c(11581),e=c(89864);let f=(0,e.createContext)({theme:"system",resolved:"dark",setTheme:()=>{}});function g(a){return"light"===a?"light":"dark"}function h({initialTheme:a,children:b}){let[c,h]=(0,e.useState)(a),[i,j]=(0,e.useState)(()=>"system"===a?"dark":a),k=(0,e.useCallback)(a=>{try{localStorage.setItem("ccdsb.theme",a)}catch{}document.cookie=`ccdsb_theme=${a}; path=/; max-age=31536000; SameSite=Lax`,h(a),j(g(a)),function(a){if("undefined"==typeof document)return;let b=document.documentElement,c=g(a);b.classList.remove("theme-light","theme-dark"),b.classList.add("light"===c?"theme-light":"theme-dark"),b.setAttribute("data-theme",c)}(a)},[]);return(0,d.jsx)(f.Provider,{value:{theme:c,resolved:i,setTheme:k},children:b})}function i(){return(0,e.useContext)(f)}},99451:(a,b,c)=>{Promise.resolve().then(c.bind(c,90639)),Promise.resolve().then(c.bind(c,49902))}};
@@ -1 +0,0 @@
1
- exports.id=212,exports.ids=[212],exports.modules={5658:(a,b,c)=>{let{createProxy:d}=c(96834);a.exports=d("/Users/zuopeng.cheng/personal/workspace/ccdsb/node_modules/.pnpm/next@15.5.15_react-dom@19.2.5_react@19.2.5__react@19.2.5/node_modules/next/dist/client/app-dir/link.js")},7119:(a,b,c)=>{"use strict";Object.defineProperty(b,"__esModule",{value:!0}),Object.defineProperty(b,"notFound",{enumerable:!0,get:function(){return e}});let d=""+c(38394).HTTP_ERROR_FALLBACK_ERROR_CODE+";404";function e(){let a=Object.defineProperty(Error(d),"__NEXT_ERROR_CODE",{value:"E394",enumerable:!1,configurable:!0});throw a.digest=d,a}("function"==typeof b.default||"object"==typeof b.default&&null!==b.default)&&void 0===b.default.__esModule&&(Object.defineProperty(b.default,"__esModule",{value:!0}),Object.assign(b.default,b),a.exports=b.default)},23819:(a,b,c)=>{"use strict";c.d(b,{Q:()=>i,n:()=>f});var d=c(80707),e=c(36118);function f({label:a,value:b,hint:c,delta:f,deltaTitle:i,progress:j,accent:k="default",className:l}){return(0,d.jsxs)("div",{className:(0,e.cn)("card card-pad flex flex-col gap-2 min-h-[128px] transition-colors","brand"===k&&"border-brand/30","success"===k&&"border-success/30","warning"===k&&"border-warning/30",l),children:[(0,d.jsx)("div",{className:"label",children:a}),(0,d.jsxs)("div",{className:"flex items-baseline gap-2 flex-wrap",children:[(0,d.jsx)("div",{className:"num-hero",children:b}),f&&Number.isFinite(f.value)&&(0,d.jsx)(g,{value:f.value,positiveIsGood:f.positiveIsGood,title:i})]}),c&&(0,d.jsx)("div",{className:"text-xs text-text-secondary mt-auto leading-snug",children:c}),j&&(0,d.jsx)("div",{className:"mt-auto pt-2",children:(0,d.jsx)(h,{value:j.value,tone:j.tone})})]})}function g({value:a,positiveIsGood:b=!0,title:c}){let f=a>=0;return(0,d.jsxs)("span",{className:(0,e.cn)("pill text-[11px] font-medium whitespace-nowrap",f===b?"text-success bg-success/10":"text-danger bg-danger/10"),title:c,children:[f?"↑":"↓"," ",Math.abs(a).toFixed(0),"%"]})}function h({value:a,tone:b="brand"}){let c=Math.max(0,Math.min(1,a));return(0,d.jsx)("div",{className:"h-1.5 w-full bg-bg-surface-hi rounded-full overflow-hidden",children:(0,d.jsx)("div",{className:(0,e.cn)("h-full rounded-full transition-all",{brand:"bg-brand",success:"bg-success",warning:"bg-warning",danger:"bg-danger"}[b]),style:{width:`${100*c}%`}})})}function i(){return(0,d.jsxs)("div",{className:"card card-pad min-h-[128px] animate-pulse",children:[(0,d.jsx)("div",{className:"h-3 w-20 bg-bg-surface-hi rounded mb-3"}),(0,d.jsx)("div",{className:"h-8 w-32 bg-bg-surface-hi rounded mb-2"}),(0,d.jsx)("div",{className:"h-3 w-24 bg-bg-surface-hi rounded mt-auto"})]})}},26353:(a,b,c)=>{"use strict";c.d(b,{pp:()=>h,qK:()=>g,wn:()=>f});var d=c(80707),e=c(36118);function f({title:a,desc:b,right:c,children:f,className:g}){return(0,d.jsxs)("section",{className:(0,e.cn)("card",g),children:[(a||c)&&(0,d.jsxs)("header",{className:"flex items-start justify-between gap-4 px-6 pt-5 pb-3 border-b border-border",children:[(0,d.jsxs)("div",{children:[a&&(0,d.jsx)("h2",{className:"text-base font-semibold text-text-primary tracking-tight",children:a}),b&&(0,d.jsx)("p",{className:"text-xs text-text-secondary mt-1",children:b})]}),c]}),(0,d.jsx)("div",{className:"p-6",children:f})]})}function g({title:a,desc:b,right:c,children:e}){return(0,d.jsxs)("div",{className:"max-w-7xl mx-auto px-6 py-8 space-y-6",children:[(0,d.jsxs)("div",{className:"flex items-end justify-between gap-4",children:[(0,d.jsxs)("div",{children:[(0,d.jsx)("h1",{className:"text-2xl font-semibold tracking-tight",children:a}),b&&(0,d.jsx)("p",{className:"text-sm text-text-secondary mt-1",children:b})]}),c]}),e]})}function h({title:a,desc:b}){return(0,d.jsxs)("div",{className:"card card-pad text-center py-16",children:[(0,d.jsx)("div",{className:"text-base font-medium text-text-secondary",children:a}),b&&(0,d.jsx)("div",{className:"text-sm text-text-tertiary mt-2",children:b})]})}},26803:(a,b,c)=>{"use strict";c.d(b,{TokenStackChart:()=>d});let d=(0,c(58979).registerClientReference)(function(){throw Error("Attempted to call TokenStackChart() from the server but TokenStackChart is on the client. It's not possible to invoke a client function from the server, it can only be rendered as a Component or passed to props of a Client Component.")},"/Users/zuopeng.cheng/personal/workspace/ccdsb/components/charts/token-stack-chart.tsx","TokenStackChart")},39393:(a,b,c)=>{Promise.resolve().then(c.bind(c,26803)),Promise.resolve().then(c.t.bind(c,5658,23))},45777:(a,b,c)=>{"use strict";Object.defineProperty(b,"__esModule",{value:!0}),Object.defineProperty(b,"unstable_rethrow",{enumerable:!0,get:function(){return d}});let d=c(74472).unstable_rethrow;("function"==typeof b.default||"object"==typeof b.default&&null!==b.default)&&void 0===b.default.__esModule&&(Object.defineProperty(b.default,"__esModule",{value:!0}),Object.assign(b.default,b),a.exports=b.default)},52545:(a,b,c)=>{Promise.resolve().then(c.bind(c,60969)),Promise.resolve().then(c.t.bind(c,3024,23))},55285:(a,b,c)=>{"use strict";function d(){throw Object.defineProperty(Error("`unauthorized()` is experimental and only allowed to be used when `experimental.authInterrupts` is enabled."),"__NEXT_ERROR_CODE",{value:"E411",enumerable:!1,configurable:!0})}Object.defineProperty(b,"__esModule",{value:!0}),Object.defineProperty(b,"unauthorized",{enumerable:!0,get:function(){return d}}),c(38394).HTTP_ERROR_FALLBACK_ERROR_CODE,("function"==typeof b.default||"object"==typeof b.default&&null!==b.default)&&void 0===b.default.__esModule&&(Object.defineProperty(b.default,"__esModule",{value:!0}),Object.assign(b.default,b),a.exports=b.default)},60969:(a,b,c)=>{"use strict";c.d(b,{TokenStackChart:()=>o});var d=c(11581),e=c(28470),f=c(82516),g=c(89596),h=c(98711),i=c(98148),j=c(98583),k=c(90053),l=c(57176),m=c(32838);let n={input:"rgb(var(--chart-input))",output:"rgb(var(--chart-output))",cacheRead:"rgb(var(--chart-cache-read))",cacheCreation:"rgb(var(--chart-cache-create))"};function o({data:a,height:b="h-72"}){let c=(0,m.kj)();return a.length?(0,d.jsxs)("div",{className:`${b} w-full`,children:[(0,d.jsx)(e.u,{width:"100%",height:"100%",children:(0,d.jsxs)(f.E,{data:a,margin:{top:12,right:8,bottom:4,left:8},children:[(0,d.jsx)(g.d,{stroke:"rgb(var(--chart-grid))",strokeDasharray:"3 3",vertical:!1}),(0,d.jsx)(h.W,{dataKey:"label",tick:{fill:"rgb(var(--chart-axis))",fontSize:11},tickLine:!1,axisLine:{stroke:"rgb(var(--chart-grid))"},interval:"preserveStartEnd",minTickGap:32}),(0,d.jsx)(i.h,{tickFormatter:a=>(0,l.jh)(Number(a)),tick:{fill:"rgb(var(--chart-axis))",fontSize:11},tickLine:!1,axisLine:{stroke:"rgb(var(--chart-grid))"},width:56}),(0,d.jsx)(j.m,{content:(0,d.jsx)(q,{}),cursor:{fill:"rgb(var(--text-primary) / 0.04)"}}),(0,d.jsx)(k.y,{dataKey:"input",stackId:"a",fill:n.input,isAnimationActive:!1}),(0,d.jsx)(k.y,{dataKey:"cacheCreation",stackId:"a",fill:n.cacheCreation,isAnimationActive:!1}),(0,d.jsx)(k.y,{dataKey:"cacheRead",stackId:"a",fill:n.cacheRead,isAnimationActive:!1}),(0,d.jsx)(k.y,{dataKey:"output",stackId:"a",fill:n.output,radius:[3,3,0,0],isAnimationActive:!1})]})}),(0,d.jsxs)("div",{className:"flex items-center flex-wrap justify-center gap-4 text-xs text-text-secondary mt-2",children:[(0,d.jsx)(p,{color:n.input,label:c("chart.legend.input")}),(0,d.jsx)(p,{color:n.cacheCreation,label:c("chart.legend.cacheWrite")}),(0,d.jsx)(p,{color:n.cacheRead,label:c("chart.legend.cacheRead")}),(0,d.jsx)(p,{color:n.output,label:c("chart.legend.output")})]})]}):(0,d.jsx)("div",{className:`${b} flex items-center justify-center text-text-tertiary text-sm`,children:c("chart.empty")})}function p({color:a,label:b}){return(0,d.jsxs)("span",{className:"inline-flex items-center gap-1.5",children:[(0,d.jsx)("span",{className:"w-2.5 h-2.5 rounded-sm",style:{background:a}}),(0,d.jsx)("span",{children:b})]})}function q(a){let b=(0,m.kj)();if(!a.active||!a.payload||!a.payload.length)return null;let c=a.payload[0].payload,e=c.input+c.output+c.cacheRead+c.cacheCreation;return(0,d.jsxs)("div",{className:"card border-border-hi shadow-lg p-3 text-xs min-w-[200px]",children:[(0,d.jsx)("div",{className:"font-medium text-text-primary mb-2",children:a.label}),(0,d.jsxs)("div",{className:"space-y-1",children:[(0,d.jsx)(r,{color:n.input,label:b("chart.legend.input"),value:c.input}),(0,d.jsx)(r,{color:n.cacheCreation,label:b("chart.legend.cacheWrite"),value:c.cacheCreation}),(0,d.jsx)(r,{color:n.cacheRead,label:b("chart.legend.cacheRead"),value:c.cacheRead}),(0,d.jsx)(r,{color:n.output,label:b("chart.legend.output"),value:c.output})]}),(0,d.jsxs)("div",{className:"mt-2 pt-2 border-t border-border flex items-center justify-between",children:[(0,d.jsx)("span",{className:"text-text-secondary",children:b("chart.tooltip.total")}),(0,d.jsx)("span",{className:"num-mono text-text-primary",children:(0,l.jh)(e)})]}),(0,d.jsxs)("div",{className:"flex items-center justify-between mt-1",children:[(0,d.jsx)("span",{className:"text-text-secondary",children:b("chart.tooltip.cost")}),(0,d.jsx)("span",{className:"num-mono text-brand",children:(0,l.az)(c.cost)})]}),(0,d.jsxs)("div",{className:"flex items-center justify-between mt-1",children:[(0,d.jsx)("span",{className:"text-text-secondary",children:b("chart.tooltip.requests")}),(0,d.jsx)("span",{className:"num-mono text-text-primary",children:c.requests})]})]})}function r({color:a,label:b,value:c}){return(0,d.jsxs)("div",{className:"flex items-center justify-between gap-3",children:[(0,d.jsxs)("span",{className:"inline-flex items-center gap-1.5 text-text-secondary",children:[(0,d.jsx)("span",{className:"w-2 h-2 rounded-sm",style:{background:a}}),b]}),(0,d.jsx)("span",{className:"num-mono text-text-primary",children:(0,l.jh)(c)})]})}},74472:(a,b,c)=>{"use strict";Object.defineProperty(b,"__esModule",{value:!0}),Object.defineProperty(b,"unstable_rethrow",{enumerable:!0,get:function(){return function a(b){if((0,g.isNextRouterError)(b)||(0,f.isBailoutToCSRError)(b)||(0,i.isDynamicServerError)(b)||(0,h.isDynamicPostpone)(b)||(0,e.isPostpone)(b)||(0,d.isHangingPromiseRejectionError)(b))throw b;b instanceof Error&&"cause"in b&&a(b.cause)}}});let d=c(88458),e=c(58263),f=c(40900),g=c(89960),h=c(29961),i=c(65725);("function"==typeof b.default||"object"==typeof b.default&&null!==b.default)&&void 0===b.default.__esModule&&(Object.defineProperty(b.default,"__esModule",{value:!0}),Object.assign(b.default,b),a.exports=b.default)},85414:(a,b,c)=>{"use strict";var d=c(92470);c.o(d,"notFound")&&c.d(b,{notFound:function(){return d.notFound}})},90666:(a,b,c)=>{"use strict";function d(){throw Object.defineProperty(Error("`forbidden()` is experimental and only allowed to be enabled when `experimental.authInterrupts` is enabled."),"__NEXT_ERROR_CODE",{value:"E488",enumerable:!1,configurable:!0})}Object.defineProperty(b,"__esModule",{value:!0}),Object.defineProperty(b,"forbidden",{enumerable:!0,get:function(){return d}}),c(38394).HTTP_ERROR_FALLBACK_ERROR_CODE,("function"==typeof b.default||"object"==typeof b.default&&null!==b.default)&&void 0===b.default.__esModule&&(Object.defineProperty(b.default,"__esModule",{value:!0}),Object.assign(b.default,b),a.exports=b.default)},92470:(a,b,c)=>{"use strict";Object.defineProperty(b,"__esModule",{value:!0}),!function(a,b){for(var c in b)Object.defineProperty(a,c,{enumerable:!0,get:b[c]})}(b,{ReadonlyURLSearchParams:function(){return k},RedirectType:function(){return e.RedirectType},forbidden:function(){return g.forbidden},notFound:function(){return f.notFound},permanentRedirect:function(){return d.permanentRedirect},redirect:function(){return d.redirect},unauthorized:function(){return h.unauthorized},unstable_isUnrecognizedActionError:function(){return l},unstable_rethrow:function(){return i.unstable_rethrow}});let d=c(96639),e=c(40312),f=c(7119),g=c(90666),h=c(55285),i=c(45777);class j extends Error{constructor(){super("Method unavailable on `ReadonlyURLSearchParams`. Read more: https://nextjs.org/docs/app/api-reference/functions/use-search-params#updating-searchparams")}}class k extends URLSearchParams{append(){throw new j}delete(){throw new j}set(){throw new j}sort(){throw new j}}function l(){throw Object.defineProperty(Error("`unstable_isUnrecognizedActionError` can only be used on the client."),"__NEXT_ERROR_CODE",{value:"E776",enumerable:!1,configurable:!0})}("function"==typeof b.default||"object"==typeof b.default&&null!==b.default)&&void 0===b.default.__esModule&&(Object.defineProperty(b.default,"__esModule",{value:!0}),Object.assign(b.default,b),a.exports=b.default)},96639:(a,b,c)=>{"use strict";Object.defineProperty(b,"__esModule",{value:!0}),!function(a,b){for(var c in b)Object.defineProperty(a,c,{enumerable:!0,get:b[c]})}(b,{getRedirectError:function(){return g},getRedirectStatusCodeFromError:function(){return l},getRedirectTypeFromError:function(){return k},getURLFromRedirectError:function(){return j},permanentRedirect:function(){return i},redirect:function(){return h}});let d=c(44570),e=c(40312),f=c(19121).actionAsyncStorage;function g(a,b,c){void 0===c&&(c=d.RedirectStatusCode.TemporaryRedirect);let f=Object.defineProperty(Error(e.REDIRECT_ERROR_CODE),"__NEXT_ERROR_CODE",{value:"E394",enumerable:!1,configurable:!0});return f.digest=e.REDIRECT_ERROR_CODE+";"+b+";"+a+";"+c+";",f}function h(a,b){var c;throw null!=b||(b=(null==f||null==(c=f.getStore())?void 0:c.isAction)?e.RedirectType.push:e.RedirectType.replace),g(a,b,d.RedirectStatusCode.TemporaryRedirect)}function i(a,b){throw void 0===b&&(b=e.RedirectType.replace),g(a,b,d.RedirectStatusCode.PermanentRedirect)}function j(a){return(0,e.isRedirectError)(a)?a.digest.split(";").slice(2,-2).join(";"):null}function k(a){if(!(0,e.isRedirectError)(a))throw Object.defineProperty(Error("Not a redirect error"),"__NEXT_ERROR_CODE",{value:"E260",enumerable:!1,configurable:!0});return a.digest.split(";",2)[1]}function l(a){if(!(0,e.isRedirectError)(a))throw Object.defineProperty(Error("Not a redirect error"),"__NEXT_ERROR_CODE",{value:"E260",enumerable:!1,configurable:!0});return Number(a.digest.split(";").at(-2))}("function"==typeof b.default||"object"==typeof b.default&&null!==b.default)&&void 0===b.default.__esModule&&(Object.defineProperty(b.default,"__esModule",{value:!0}),Object.assign(b.default,b),a.exports=b.default)}};