claude-usage-dashboard 1.3.3 → 1.3.5

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/bin/cli.js CHANGED
@@ -1,20 +1,23 @@
1
- #!/usr/bin/env node
2
- import { spawn } from 'child_process';
3
- import { fileURLToPath } from 'url';
4
- import path from 'path';
5
-
6
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
7
- const server = path.join(__dirname, '..', 'server', 'index.js');
8
-
9
- const child = spawn(process.execPath, [server], {
10
- stdio: 'inherit',
11
- // Attach child to the terminal's foreground process group
12
- detached: false,
13
- });
14
-
15
- child.on('exit', (code) => process.exit(code ?? 0));
16
-
17
- // Forward signals to child
18
- for (const sig of ['SIGINT', 'SIGTERM']) {
19
- process.on(sig, () => child.kill(sig));
20
- }
1
+ #!/usr/bin/env node
2
+ import { spawn } from 'child_process';
3
+ import { fileURLToPath } from 'url';
4
+ import path from 'path';
5
+
6
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
7
+ const server = path.join(__dirname, '..', 'server', 'index.js');
8
+
9
+ const child = spawn(process.execPath, [server], {
10
+ stdio: 'inherit',
11
+ // Attach child to the terminal's foreground process group
12
+ detached: false,
13
+ });
14
+
15
+ child.on('exit', (code) => process.exit(code ?? 0));
16
+
17
+ // Forward signals to child, then exit parent
18
+ for (const sig of ['SIGINT', 'SIGTERM']) {
19
+ process.on(sig, () => {
20
+ child.kill(sig);
21
+ process.exit();
22
+ });
23
+ }
package/package.json CHANGED
@@ -1,40 +1,40 @@
1
- {
2
- "name": "claude-usage-dashboard",
3
- "version": "1.3.3",
4
- "description": "Dashboard that visualizes Claude Code usage from local session logs",
5
- "main": "server/index.js",
6
- "bin": {
7
- "claude-usage-dashboard": "bin/cli.js"
8
- },
9
- "files": [
10
- "bin/",
11
- "server/",
12
- "public/"
13
- ],
14
- "scripts": {
15
- "start": "node server/index.js",
16
- "test": "mocha test/**/*.test.js --timeout 5000"
17
- },
18
- "keywords": [
19
- "claude",
20
- "usage",
21
- "dashboard",
22
- "token",
23
- "cost"
24
- ],
25
- "author": "",
26
- "license": "ISC",
27
- "repository": {
28
- "type": "git",
29
- "url": "https://github.com/ludengz/claude-usage-dashboard.git"
30
- },
31
- "type": "module",
32
- "dependencies": {
33
- "d3": "^7.9.0",
34
- "express": "^5.2.1"
35
- },
36
- "devDependencies": {
37
- "chai": "^6.2.2",
38
- "mocha": "^11.7.5"
39
- }
40
- }
1
+ {
2
+ "name": "claude-usage-dashboard",
3
+ "version": "1.3.5",
4
+ "description": "Dashboard that visualizes Claude Code usage from local session logs",
5
+ "main": "server/index.js",
6
+ "bin": {
7
+ "claude-usage-dashboard": "bin/cli.js"
8
+ },
9
+ "files": [
10
+ "bin/",
11
+ "server/",
12
+ "public/"
13
+ ],
14
+ "scripts": {
15
+ "start": "node server/index.js",
16
+ "test": "mocha test/**/*.test.js --timeout 5000"
17
+ },
18
+ "keywords": [
19
+ "claude",
20
+ "usage",
21
+ "dashboard",
22
+ "token",
23
+ "cost"
24
+ ],
25
+ "author": "",
26
+ "license": "ISC",
27
+ "repository": {
28
+ "type": "git",
29
+ "url": "https://github.com/ludengz/claude-usage-dashboard.git"
30
+ },
31
+ "type": "module",
32
+ "dependencies": {
33
+ "d3": "^7.9.0",
34
+ "express": "^5.2.1"
35
+ },
36
+ "devDependencies": {
37
+ "chai": "^6.2.2",
38
+ "mocha": "^11.7.5"
39
+ }
40
+ }
@@ -1,264 +1,265 @@
1
- * { margin: 0; padding: 0; box-sizing: border-box; }
2
-
3
- :root {
4
- --bg-primary: #0f172a;
5
- --bg-card: #1e293b;
6
- --bg-input: #334155;
7
- --border: #475569;
8
- --text-primary: #f8fafc;
9
- --text-secondary: #94a3b8;
10
- --text-muted: #64748b;
11
- --blue: #3b82f6;
12
- --blue-light: #60a5fa;
13
- --purple: #8b5cf6;
14
- --orange: #f97316;
15
- --amber: #f59e0b;
16
- --green: #4ade80;
17
- --red: #ef4444;
18
- }
19
-
20
- body {
21
- background: var(--bg-primary);
22
- color: var(--text-primary);
23
- font-family: system-ui, -apple-system, sans-serif;
24
- padding: 0 24px 40px;
25
- }
26
-
27
- .top-bar {
28
- display: flex;
29
- justify-content: space-between;
30
- align-items: center;
31
- padding: 16px 0;
32
- border-bottom: 1px solid var(--bg-card);
33
- margin-bottom: 20px;
34
- }
35
- .logo { font-size: 18px; font-weight: 700; }
36
- .controls { display: flex; gap: 12px; align-items: center; }
37
-
38
- .summary-cards {
39
- display: grid;
40
- grid-template-columns: repeat(3, 1fr);
41
- gap: 12px;
42
- margin-bottom: 20px;
43
- }
44
- .card {
45
- background: var(--bg-card);
46
- border-radius: 8px;
47
- padding: 16px;
48
- }
49
- .card-label {
50
- font-size: 11px;
51
- color: var(--text-muted);
52
- text-transform: uppercase;
53
- letter-spacing: 1px;
54
- }
55
- .card-value {
56
- font-size: 24px;
57
- font-weight: 700;
58
- margin-top: 4px;
59
- }
60
- .card-sub {
61
- font-size: 11px;
62
- color: var(--text-secondary);
63
- margin-top: 2px;
64
- }
65
- #val-api-cost { color: var(--amber); }
66
- #val-savings { color: var(--green); }
67
- #val-cache-rate { color: var(--blue-light); }
68
-
69
- .chart-section {
70
- background: var(--bg-card);
71
- border-radius: 8px;
72
- padding: 20px;
73
- margin-bottom: 12px;
74
- }
75
- .chart-section h2 {
76
- font-size: 14px;
77
- font-weight: 600;
78
- margin-bottom: 16px;
79
- }
80
- .chart-header {
81
- display: flex;
82
- justify-content: space-between;
83
- align-items: center;
84
- margin-bottom: 16px;
85
- }
86
- .chart-header h2 { margin-bottom: 0; }
87
- .chart-container { min-height: 200px; }
88
-
89
- .chart-row-3 {
90
- display: grid;
91
- grid-template-columns: repeat(3, 1fr);
92
- gap: 12px;
93
- margin-bottom: 12px;
94
- }
95
-
96
- .granularity-toggle { display: flex; gap: 4px; }
97
- .granularity-toggle button {
98
- padding: 4px 12px;
99
- background: var(--bg-input);
100
- border: none;
101
- border-radius: 4px;
102
- color: var(--text-secondary);
103
- font-size: 12px;
104
- cursor: pointer;
105
- }
106
- .granularity-toggle button.active {
107
- background: var(--blue);
108
- color: white;
109
- box-shadow: 0 0 0 1px rgba(59,130,246,0.5);
110
- }
111
- .granularity-toggle button:disabled {
112
- opacity: 0.35;
113
- cursor: not-allowed;
114
- }
115
-
116
- .table-controls { display: flex; gap: 8px; }
117
- .filter-input, .sort-select {
118
- padding: 6px 10px;
119
- background: var(--bg-input);
120
- border: 1px solid var(--border);
121
- border-radius: 6px;
122
- color: var(--text-primary);
123
- font-size: 12px;
124
- }
125
- .filter-input { width: 180px; }
126
-
127
- .table-container { overflow-x: auto; }
128
- .table-container table {
129
- width: 100%;
130
- border-collapse: collapse;
131
- font-size: 12px;
132
- }
133
- .table-container th {
134
- text-align: left;
135
- padding: 10px 8px;
136
- border-bottom: 2px solid var(--bg-input);
137
- color: var(--text-muted);
138
- text-transform: uppercase;
139
- font-size: 10px;
140
- letter-spacing: 1px;
141
- cursor: pointer;
142
- }
143
- .table-container th.align-right,
144
- .table-container td.align-right { text-align: right; }
145
- .table-container td {
146
- padding: 10px 8px;
147
- border-bottom: 1px solid var(--bg-primary);
148
- color: var(--text-secondary);
149
- }
150
- .table-container tfoot td {
151
- border-top: 2px solid var(--bg-input);
152
- font-weight: 600;
153
- color: var(--text-primary);
154
- }
155
-
156
- .tag {
157
- display: inline-block;
158
- padding: 2px 8px;
159
- border-radius: 4px;
160
- font-size: 11px;
161
- }
162
- .tag-project { background: #1e3a5f; color: var(--blue-light); }
163
- .tag-model-sonnet { background: #1e3a5f; color: var(--blue-light); }
164
- .tag-model-opus { background: #3b1764; color: #c084fc; }
165
- .tag-model-haiku { background: #1a2e1a; color: var(--green); }
166
-
167
- .pagination {
168
- display: flex;
169
- justify-content: center;
170
- gap: 4px;
171
- margin-top: 12px;
172
- }
173
- .pagination button {
174
- padding: 4px 10px;
175
- background: var(--bg-input);
176
- border: none;
177
- border-radius: 4px;
178
- font-size: 11px;
179
- color: var(--text-secondary);
180
- cursor: pointer;
181
- }
182
- .pagination button.active {
183
- background: var(--blue);
184
- color: white;
185
- }
186
-
187
- .date-picker {
188
- display: flex;
189
- align-items: center;
190
- gap: 8px;
191
- }
192
- .date-picker input {
193
- padding: 6px 10px;
194
- background: var(--bg-input);
195
- border: 1px solid var(--border);
196
- border-radius: 6px;
197
- color: var(--text-primary);
198
- font-size: 12px;
199
- }
200
- .date-picker span { color: var(--text-secondary); font-size: 12px; }
201
-
202
- .refresh-controls {
203
- display: flex;
204
- align-items: center;
205
- gap: 8px;
206
- }
207
- .btn-refresh {
208
- padding: 4px 10px;
209
- background: var(--bg-input);
210
- border: 1px solid var(--border);
211
- border-radius: 6px;
212
- color: var(--text-primary);
213
- font-size: 16px;
214
- cursor: pointer;
215
- line-height: 1;
216
- }
217
- .btn-refresh:hover { background: var(--blue); }
218
- .auto-refresh-label {
219
- display: flex;
220
- align-items: center;
221
- gap: 4px;
222
- font-size: 12px;
223
- color: var(--text-secondary);
224
- cursor: pointer;
225
- }
226
- .last-updated {
227
- font-size: 11px;
228
- color: var(--text-muted);
229
- white-space: nowrap;
230
- }
231
-
232
- .plan-selector select, .plan-selector input {
233
- padding: 6px 10px;
234
- background: var(--bg-input);
235
- border: 1px solid var(--border);
236
- border-radius: 6px;
237
- color: var(--text-primary);
238
- font-size: 12px;
239
- }
240
-
241
- .d3-tooltip {
242
- position: absolute;
243
- padding: 8px 12px;
244
- background: rgba(15, 23, 42, 0.95);
245
- border: 1px solid var(--border);
246
- border-radius: 6px;
247
- font-size: 12px;
248
- color: var(--text-primary);
249
- pointer-events: none;
250
- z-index: 100;
251
- }
252
-
253
- .quota-unavailable {
254
- color: var(--text-muted);
255
- font-size: 13px;
256
- padding: 20px;
257
- text-align: center;
258
- }
259
- #quota-section .chart-container { min-height: auto; }
260
-
261
- @media (max-width: 768px) {
262
- .summary-cards { grid-template-columns: repeat(2, 1fr); }
263
- .chart-row-3 { grid-template-columns: 1fr; }
264
- }
1
+ * { margin: 0; padding: 0; box-sizing: border-box; }
2
+
3
+ :root {
4
+ --bg-primary: #0f172a;
5
+ --bg-card: #1e293b;
6
+ --bg-input: #334155;
7
+ --border: #475569;
8
+ --text-primary: #f8fafc;
9
+ --text-secondary: #94a3b8;
10
+ --text-muted: #64748b;
11
+ --blue: #3b82f6;
12
+ --blue-light: #60a5fa;
13
+ --purple: #8b5cf6;
14
+ --orange: #f97316;
15
+ --amber: #f59e0b;
16
+ --green: #4ade80;
17
+ --red: #ef4444;
18
+ }
19
+
20
+ body {
21
+ background: var(--bg-primary);
22
+ color: var(--text-primary);
23
+ font-family: system-ui, -apple-system, sans-serif;
24
+ padding: 0 24px 40px;
25
+ }
26
+
27
+ .top-bar {
28
+ display: flex;
29
+ justify-content: space-between;
30
+ align-items: center;
31
+ padding: 16px 0;
32
+ border-bottom: 1px solid var(--bg-card);
33
+ margin-bottom: 20px;
34
+ }
35
+ .logo { font-size: 18px; font-weight: 700; display: flex; align-items: center; gap: 8px; }
36
+ .claude-icon { width: 22px; height: 22px; flex-shrink: 0; }
37
+ .controls { display: flex; gap: 12px; align-items: center; }
38
+
39
+ .summary-cards {
40
+ display: grid;
41
+ grid-template-columns: repeat(3, 1fr);
42
+ gap: 12px;
43
+ margin-bottom: 20px;
44
+ }
45
+ .card {
46
+ background: var(--bg-card);
47
+ border-radius: 8px;
48
+ padding: 16px;
49
+ }
50
+ .card-label {
51
+ font-size: 11px;
52
+ color: var(--text-muted);
53
+ text-transform: uppercase;
54
+ letter-spacing: 1px;
55
+ }
56
+ .card-value {
57
+ font-size: 24px;
58
+ font-weight: 700;
59
+ margin-top: 4px;
60
+ }
61
+ .card-sub {
62
+ font-size: 11px;
63
+ color: var(--text-secondary);
64
+ margin-top: 2px;
65
+ }
66
+ #val-api-cost { color: var(--amber); }
67
+ #val-savings { color: var(--green); }
68
+ #val-cache-rate { color: var(--blue-light); }
69
+
70
+ .chart-section {
71
+ background: var(--bg-card);
72
+ border-radius: 8px;
73
+ padding: 20px;
74
+ margin-bottom: 12px;
75
+ }
76
+ .chart-section h2 {
77
+ font-size: 14px;
78
+ font-weight: 600;
79
+ margin-bottom: 16px;
80
+ }
81
+ .chart-header {
82
+ display: flex;
83
+ justify-content: space-between;
84
+ align-items: center;
85
+ margin-bottom: 16px;
86
+ }
87
+ .chart-header h2 { margin-bottom: 0; }
88
+ .chart-container { min-height: 200px; }
89
+
90
+ .chart-row-3 {
91
+ display: grid;
92
+ grid-template-columns: repeat(3, 1fr);
93
+ gap: 12px;
94
+ margin-bottom: 12px;
95
+ }
96
+
97
+ .granularity-toggle { display: flex; gap: 4px; }
98
+ .granularity-toggle button {
99
+ padding: 4px 12px;
100
+ background: var(--bg-input);
101
+ border: none;
102
+ border-radius: 4px;
103
+ color: var(--text-secondary);
104
+ font-size: 12px;
105
+ cursor: pointer;
106
+ }
107
+ .granularity-toggle button.active {
108
+ background: var(--blue);
109
+ color: white;
110
+ box-shadow: 0 0 0 1px rgba(59,130,246,0.5);
111
+ }
112
+ .granularity-toggle button:disabled {
113
+ opacity: 0.35;
114
+ cursor: not-allowed;
115
+ }
116
+
117
+ .table-controls { display: flex; gap: 8px; }
118
+ .filter-input, .sort-select {
119
+ padding: 6px 10px;
120
+ background: var(--bg-input);
121
+ border: 1px solid var(--border);
122
+ border-radius: 6px;
123
+ color: var(--text-primary);
124
+ font-size: 12px;
125
+ }
126
+ .filter-input { width: 180px; }
127
+
128
+ .table-container { overflow-x: auto; }
129
+ .table-container table {
130
+ width: 100%;
131
+ border-collapse: collapse;
132
+ font-size: 12px;
133
+ }
134
+ .table-container th {
135
+ text-align: left;
136
+ padding: 10px 8px;
137
+ border-bottom: 2px solid var(--bg-input);
138
+ color: var(--text-muted);
139
+ text-transform: uppercase;
140
+ font-size: 10px;
141
+ letter-spacing: 1px;
142
+ cursor: pointer;
143
+ }
144
+ .table-container th.align-right,
145
+ .table-container td.align-right { text-align: right; }
146
+ .table-container td {
147
+ padding: 10px 8px;
148
+ border-bottom: 1px solid var(--bg-primary);
149
+ color: var(--text-secondary);
150
+ }
151
+ .table-container tfoot td {
152
+ border-top: 2px solid var(--bg-input);
153
+ font-weight: 600;
154
+ color: var(--text-primary);
155
+ }
156
+
157
+ .tag {
158
+ display: inline-block;
159
+ padding: 2px 8px;
160
+ border-radius: 4px;
161
+ font-size: 11px;
162
+ }
163
+ .tag-project { background: #1e3a5f; color: var(--blue-light); }
164
+ .tag-model-sonnet { background: #1e3a5f; color: var(--blue-light); }
165
+ .tag-model-opus { background: #3b1764; color: #c084fc; }
166
+ .tag-model-haiku { background: #1a2e1a; color: var(--green); }
167
+
168
+ .pagination {
169
+ display: flex;
170
+ justify-content: center;
171
+ gap: 4px;
172
+ margin-top: 12px;
173
+ }
174
+ .pagination button {
175
+ padding: 4px 10px;
176
+ background: var(--bg-input);
177
+ border: none;
178
+ border-radius: 4px;
179
+ font-size: 11px;
180
+ color: var(--text-secondary);
181
+ cursor: pointer;
182
+ }
183
+ .pagination button.active {
184
+ background: var(--blue);
185
+ color: white;
186
+ }
187
+
188
+ .date-picker {
189
+ display: flex;
190
+ align-items: center;
191
+ gap: 8px;
192
+ }
193
+ .date-picker input {
194
+ padding: 6px 10px;
195
+ background: var(--bg-input);
196
+ border: 1px solid var(--border);
197
+ border-radius: 6px;
198
+ color: var(--text-primary);
199
+ font-size: 12px;
200
+ }
201
+ .date-picker span { color: var(--text-secondary); font-size: 12px; }
202
+
203
+ .refresh-controls {
204
+ display: flex;
205
+ align-items: center;
206
+ gap: 8px;
207
+ }
208
+ .btn-refresh {
209
+ padding: 4px 10px;
210
+ background: var(--bg-input);
211
+ border: 1px solid var(--border);
212
+ border-radius: 6px;
213
+ color: var(--text-primary);
214
+ font-size: 16px;
215
+ cursor: pointer;
216
+ line-height: 1;
217
+ }
218
+ .btn-refresh:hover { background: var(--blue); }
219
+ .auto-refresh-label {
220
+ display: flex;
221
+ align-items: center;
222
+ gap: 4px;
223
+ font-size: 12px;
224
+ color: var(--text-secondary);
225
+ cursor: pointer;
226
+ }
227
+ .last-updated {
228
+ font-size: 11px;
229
+ color: var(--text-muted);
230
+ white-space: nowrap;
231
+ }
232
+
233
+ .plan-selector select, .plan-selector input {
234
+ padding: 6px 10px;
235
+ background: var(--bg-input);
236
+ border: 1px solid var(--border);
237
+ border-radius: 6px;
238
+ color: var(--text-primary);
239
+ font-size: 12px;
240
+ }
241
+
242
+ .d3-tooltip {
243
+ position: absolute;
244
+ padding: 8px 12px;
245
+ background: rgba(15, 23, 42, 0.95);
246
+ border: 1px solid var(--border);
247
+ border-radius: 6px;
248
+ font-size: 12px;
249
+ color: var(--text-primary);
250
+ pointer-events: none;
251
+ z-index: 100;
252
+ }
253
+
254
+ .quota-unavailable {
255
+ color: var(--text-muted);
256
+ font-size: 13px;
257
+ padding: 20px;
258
+ text-align: center;
259
+ }
260
+ #quota-section .chart-container { min-height: auto; }
261
+
262
+ @media (max-width: 768px) {
263
+ .summary-cards { grid-template-columns: repeat(2, 1fr); }
264
+ .chart-row-3 { grid-template-columns: 1fr; }
265
+ }