claude-usage-dashboard 1.4.0 → 1.4.2

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.
@@ -1,117 +1,117 @@
1
- function formatTokens(n) {
2
- if (n >= 1_000_000) return (n / 1_000_000).toFixed(1) + 'M';
3
- if (n >= 1_000) return (n / 1_000).toFixed(0) + 'K';
4
- return n.toString();
5
- }
6
-
7
- const MODEL_DISPLAY = {
8
- 'claude-opus-4-6': 'opus 4.6',
9
- 'claude-sonnet-4-6': 'sonnet 4.6',
10
- 'claude-haiku-4-5': 'haiku 4.5',
11
- 'claude-haiku-4-5-20251001': 'haiku 4.5',
12
- };
13
-
14
- function modelTag(model) {
15
- const shortName = MODEL_DISPLAY[model] || model.replace('claude-', '').replace(/-(\d+)-(\d+)/, ' $1.$2');
16
- let cls = 'tag-model-sonnet';
17
- if (model.includes('opus')) cls = 'tag-model-opus';
18
- else if (model.includes('haiku')) cls = 'tag-model-haiku';
19
- return `<span class="tag ${cls}">${shortName}</span>`;
20
- }
21
-
22
- function formatDate(iso) {
23
- const d = new Date(iso);
24
- return d.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) +
25
- ', ' + d.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit', hour12: false });
26
- }
27
-
28
- function formatDuration(minutes) {
29
- if (minutes < 60) return `${minutes}m`;
30
- const h = Math.floor(minutes / 60);
31
- const m = minutes % 60;
32
- return `${h}h ${m}m`;
33
- }
34
-
35
- export function renderSessionTable(container, data, { onSort, onPageChange }) {
36
- container.innerHTML = '';
37
-
38
- const table = document.createElement('table');
39
- const thead = document.createElement('thead');
40
- const headerRow = document.createElement('tr');
41
-
42
- const columns = [
43
- { key: 'date', label: 'Date & Time' },
44
- { key: 'project', label: 'Project' },
45
- { key: 'models', label: 'Model(s)' },
46
- { key: 'input', label: 'Input', align: 'right' },
47
- { key: 'output', label: 'Output', align: 'right' },
48
- { key: 'cache_read', label: 'Cache Read', align: 'right' },
49
- { key: 'cache_creation', label: 'Cache Write', align: 'right' },
50
- { key: 'total', label: 'Total', align: 'right' },
51
- { key: 'cost', label: 'API Cost', align: 'right' },
52
- { key: 'duration', label: 'Duration', align: 'right' },
53
- ];
54
-
55
- for (const col of columns) {
56
- const th = document.createElement('th');
57
- th.textContent = col.label;
58
- if (col.align) th.className = 'align-right';
59
- if (['date', 'cost', 'total'].includes(col.key)) {
60
- th.style.cursor = 'pointer';
61
- th.addEventListener('click', () => {
62
- const sortKey = col.key === 'total' ? 'tokens' : col.key;
63
- onSort(sortKey);
64
- });
65
- }
66
- headerRow.appendChild(th);
67
- }
68
- thead.appendChild(headerRow);
69
- table.appendChild(thead);
70
-
71
- const tbody = document.createElement('tbody');
72
- for (const s of data.sessions) {
73
- const tr = document.createElement('tr');
74
- tr.innerHTML = `
75
- <td>${formatDate(s.startTime)}</td>
76
- <td><span class="tag tag-project">${s.project}</span></td>
77
- <td>${s.models.map(modelTag).join(' ')}</td>
78
- <td class="align-right" style="color:#60a5fa">${formatTokens(s.input_tokens)}</td>
79
- <td class="align-right" style="color:#f97316">${formatTokens(s.output_tokens)}</td>
80
- <td class="align-right" style="color:#4ade80">${formatTokens(s.cache_read_tokens)}</td>
81
- <td class="align-right" style="color:#f59e0b">${formatTokens(s.cache_creation_tokens)}</td>
82
- <td class="align-right" style="font-weight:600">${formatTokens(s.total_tokens)}</td>
83
- <td class="align-right" style="color:#f59e0b;font-weight:600">$${s.estimated_cost_usd.toFixed(2)}</td>
84
- <td class="align-right">${formatDuration(s.duration_minutes)}</td>
85
- `;
86
- tbody.appendChild(tr);
87
- }
88
- table.appendChild(tbody);
89
-
90
- if (data.totals) {
91
- const tfoot = document.createElement('tfoot');
92
- const tr = document.createElement('tr');
93
- tr.innerHTML = `
94
- <td colspan="3">Showing ${data.sessions.length} of ${data.pagination.total_sessions} sessions</td>
95
- <td class="align-right" colspan="4"></td>
96
- <td class="align-right">${formatTokens(data.totals.total_tokens)}</td>
97
- <td class="align-right" style="color:#f59e0b">$${data.totals.estimated_cost_usd.toFixed(2)}</td>
98
- <td></td>
99
- `;
100
- tfoot.appendChild(tr);
101
- table.appendChild(tfoot);
102
- }
103
-
104
- container.appendChild(table);
105
-
106
- const pagEl = document.getElementById('session-pagination');
107
- if (pagEl && data.pagination && data.pagination.total_pages > 1) {
108
- pagEl.innerHTML = '';
109
- for (let i = 1; i <= data.pagination.total_pages; i++) {
110
- const btn = document.createElement('button');
111
- btn.textContent = i;
112
- if (i === data.pagination.page) btn.className = 'active';
113
- btn.addEventListener('click', () => onPageChange(i));
114
- pagEl.appendChild(btn);
115
- }
116
- }
117
- }
1
+ function formatTokens(n) {
2
+ if (n >= 1_000_000) return (n / 1_000_000).toFixed(1) + 'M';
3
+ if (n >= 1_000) return (n / 1_000).toFixed(0) + 'K';
4
+ return n.toString();
5
+ }
6
+
7
+ const MODEL_DISPLAY = {
8
+ 'claude-opus-4-6': 'opus 4.6',
9
+ 'claude-sonnet-4-6': 'sonnet 4.6',
10
+ 'claude-haiku-4-5': 'haiku 4.5',
11
+ 'claude-haiku-4-5-20251001': 'haiku 4.5',
12
+ };
13
+
14
+ function modelTag(model) {
15
+ const shortName = MODEL_DISPLAY[model] || model.replace('claude-', '').replace(/-(\d+)-(\d+)/, ' $1.$2');
16
+ let cls = 'tag-model-sonnet';
17
+ if (model.includes('opus')) cls = 'tag-model-opus';
18
+ else if (model.includes('haiku')) cls = 'tag-model-haiku';
19
+ return `<span class="tag ${cls}">${shortName}</span>`;
20
+ }
21
+
22
+ function formatDate(iso) {
23
+ const d = new Date(iso);
24
+ return d.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) +
25
+ ', ' + d.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit', hour12: false });
26
+ }
27
+
28
+ function formatDuration(minutes) {
29
+ if (minutes < 60) return `${minutes}m`;
30
+ const h = Math.floor(minutes / 60);
31
+ const m = minutes % 60;
32
+ return `${h}h ${m}m`;
33
+ }
34
+
35
+ export function renderSessionTable(container, data, { onSort, onPageChange }) {
36
+ container.innerHTML = '';
37
+
38
+ const table = document.createElement('table');
39
+ const thead = document.createElement('thead');
40
+ const headerRow = document.createElement('tr');
41
+
42
+ const columns = [
43
+ { key: 'date', label: 'Date & Time' },
44
+ { key: 'project', label: 'Project' },
45
+ { key: 'models', label: 'Model(s)' },
46
+ { key: 'input', label: 'Input', align: 'right' },
47
+ { key: 'output', label: 'Output', align: 'right' },
48
+ { key: 'cache_read', label: 'Cache Read', align: 'right' },
49
+ { key: 'cache_creation', label: 'Cache Write', align: 'right' },
50
+ { key: 'total', label: 'Total', align: 'right' },
51
+ { key: 'cost', label: 'API Cost', align: 'right' },
52
+ { key: 'duration', label: 'Duration', align: 'right' },
53
+ ];
54
+
55
+ for (const col of columns) {
56
+ const th = document.createElement('th');
57
+ th.textContent = col.label;
58
+ if (col.align) th.className = 'align-right';
59
+ if (['date', 'cost', 'total'].includes(col.key)) {
60
+ th.style.cursor = 'pointer';
61
+ th.addEventListener('click', () => {
62
+ const sortKey = col.key === 'total' ? 'tokens' : col.key;
63
+ onSort(sortKey);
64
+ });
65
+ }
66
+ headerRow.appendChild(th);
67
+ }
68
+ thead.appendChild(headerRow);
69
+ table.appendChild(thead);
70
+
71
+ const tbody = document.createElement('tbody');
72
+ for (const s of data.sessions) {
73
+ const tr = document.createElement('tr');
74
+ tr.innerHTML = `
75
+ <td>${formatDate(s.startTime)}</td>
76
+ <td><span class="tag tag-project">${s.project}</span></td>
77
+ <td>${s.models.map(modelTag).join(' ')}</td>
78
+ <td class="align-right" style="color:#60a5fa">${formatTokens(s.input_tokens)}</td>
79
+ <td class="align-right" style="color:#f97316">${formatTokens(s.output_tokens)}</td>
80
+ <td class="align-right" style="color:#4ade80">${formatTokens(s.cache_read_tokens)}</td>
81
+ <td class="align-right" style="color:#f59e0b">${formatTokens(s.cache_creation_tokens)}</td>
82
+ <td class="align-right" style="font-weight:600">${formatTokens(s.total_tokens)}</td>
83
+ <td class="align-right" style="color:#f59e0b;font-weight:600">$${s.estimated_cost_usd.toFixed(2)}</td>
84
+ <td class="align-right">${formatDuration(s.duration_minutes)}</td>
85
+ `;
86
+ tbody.appendChild(tr);
87
+ }
88
+ table.appendChild(tbody);
89
+
90
+ if (data.totals) {
91
+ const tfoot = document.createElement('tfoot');
92
+ const tr = document.createElement('tr');
93
+ tr.innerHTML = `
94
+ <td colspan="3">Showing ${data.sessions.length} of ${data.pagination.total_sessions} sessions</td>
95
+ <td class="align-right" colspan="4"></td>
96
+ <td class="align-right">${formatTokens(data.totals.total_tokens)}</td>
97
+ <td class="align-right" style="color:#f59e0b">$${data.totals.estimated_cost_usd.toFixed(2)}</td>
98
+ <td></td>
99
+ `;
100
+ tfoot.appendChild(tr);
101
+ table.appendChild(tfoot);
102
+ }
103
+
104
+ container.appendChild(table);
105
+
106
+ const pagEl = document.getElementById('session-pagination');
107
+ if (pagEl && data.pagination && data.pagination.total_pages > 1) {
108
+ pagEl.innerHTML = '';
109
+ for (let i = 1; i <= data.pagination.total_pages; i++) {
110
+ const btn = document.createElement('button');
111
+ btn.textContent = i;
112
+ if (i === data.pagination.page) btn.className = 'active';
113
+ btn.addEventListener('click', () => onPageChange(i));
114
+ pagEl.appendChild(btn);
115
+ }
116
+ }
117
+ }