Skip to content

Commit 9b3c206

Browse files
committed
Release v1.2.4
2 parents be28e2a + 31d9a18 commit 9b3c206

7 files changed

Lines changed: 212 additions & 68 deletions

File tree

CHANGELOG.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,23 @@
11
# Changelog
22

3+
## v1.2.4 — 2026-05-30
4+
5+
### Dashboard
6+
7+
- Recoloured the dashboard to a warm, neutral palette aligned with the Claude Code interface (less blue): page `#161617`, cards `#1E1F20`, lighter text `#BFBFBF`, plus a `--raised` hover layer and a dedicated `--red`. Switched to an elevated layering model — cards sit *above* the page (lighter), hover lighter still — replacing the previous inverted scheme.
8+
- Reworked chart colours: a warm, Anthropic-leaning donut palette (clay/tan/sage/dusty-blue/…); token series are blue / coral / green / amber; chart axis & legend text use a slightly lighter shade so small labels stay legible on the dark cards.
9+
- Chart hovers now lighten consistently (bars/series go to full opacity; the doughnut pops the hovered slice). Tooltip colour swatches are solid and borderless — removed Chart.js's default white `multiKeyBackground` edge. The hourly chart's legend/tooltip use a coral circle for the output line and a blue square for the turns bars.
10+
- Legend series toggles now persist across repaints (filter changes, auto-refresh, sorting) for every chart, including per-slice visibility on the doughnut.
11+
- Header title and gauge icon now use the lightest text colour (neutral) instead of coral. Selected model chips use a neutral background with a coral border; range / timezone tabs use a neutral selected background (no orange).
12+
- Cost values use thousand separators (e.g. `$1,050.49`).
13+
- Header meta puts "Auto-refresh in 30s" on its own line, with more space before the Rescan button.
14+
- Removed the ⚡ peak-hour markers from the hourly axis labels (they collided with the axis; peak hours are still shown by the red bars and the legend).
15+
- Fixed a stale "Apr 2026" stat sublabel → "May 2026".
16+
17+
### Extension
18+
19+
- The loading / status screen now matches the dashboard header — same gauge icon (served via a webview URI), title, and elevated-palette colours — instead of the old coral heading on a mismatched background.
20+
321
## v1.2.3 — 2026-05-30
422

523
### Extension

dashboard.py

Lines changed: 140 additions & 53 deletions
Large diffs are not rendered by default.

vscode-extension/package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vscode-extension/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "claude-usage-phuryn",
33
"displayName": "Claude Code Usage by Paweł Huryn",
44
"description": "Embed your Claude Code usage dashboard (token counts, costs, sessions, projects) directly inside VS Code. Reads local JSONL transcripts, no API calls.",
5-
"version": "1.2.3",
5+
"version": "1.2.4",
66
"publisher": "PawelHuryn",
77
"author": {
88
"name": "Paweł Huryn",

vscode-extension/src/extension.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ class Extension {
3030
// to spawn the server. openDashboard() coalesces repeat calls.
3131
this.sidebar = new DashboardSidebar(() => {
3232
void this.openDashboard();
33-
});
33+
}, context.extensionUri);
3434

3535
context.subscriptions.push(
3636
this.output,

vscode-extension/src/sidebar.ts

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,13 @@ import { ServerManager } from "./server-manager";
1818
* (a Blob + `a.download` click) works inside the webview — without it Chromium
1919
* silently blocks the download.
2020
*/
21-
export function renderHtml(url: string | null, statusText: string, nonce: string): string {
21+
export function renderHtml(
22+
url: string | null,
23+
statusText: string,
24+
nonce: string,
25+
iconUri = "",
26+
cspSource = "",
27+
): string {
2228
if (url) {
2329
return `<!DOCTYPE html>
2430
<html lang="en">
@@ -28,7 +34,7 @@ export function renderHtml(url: string | null, statusText: string, nonce: string
2834
content="default-src 'none'; frame-src http://127.0.0.1:* http://localhost:*; style-src 'unsafe-inline'; script-src 'nonce-${nonce}';">
2935
<title>Claude Usage</title>
3036
<style>
31-
html, body { margin: 0; padding: 0; height: 100%; background: #191A1B; }
37+
html, body { margin: 0; padding: 0; height: 100%; background: #161617; }
3238
iframe { border: 0; width: 100%; height: 100vh; display: block; }
3339
</style>
3440
</head>
@@ -38,24 +44,31 @@ export function renderHtml(url: string | null, statusText: string, nonce: string
3844
</html>`;
3945
}
4046

47+
// Status / loading pane — styled to match the dashboard header (same icon,
48+
// title, and elevated-palette colors) so the cold-start screen doesn't jar.
49+
const imgSrc = cspSource ? ` img-src ${cspSource};` : "";
50+
const logo = iconUri ? `<span class="logo"></span>` : "";
4151
return `<!DOCTYPE html>
4252
<html lang="en">
4353
<head>
4454
<meta charset="UTF-8">
4555
<meta http-equiv="Content-Security-Policy"
46-
content="default-src 'none'; style-src 'unsafe-inline'; script-src 'nonce-${nonce}';">
56+
content="default-src 'none';${imgSrc} style-src 'unsafe-inline'; script-src 'nonce-${nonce}';">
4757
<title>Claude Usage</title>
4858
<style>
49-
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; color: #e2e8f0; background: #191A1B; padding: 24px; line-height: 1.5; }
50-
h2 { color: #d97757; font-weight: 600; font-size: 14px; text-transform: uppercase; letter-spacing: 0.05em; margin: 0 0 16px; }
51-
pre { background: #1a1d27; border: 1px solid #2a2d3a; border-radius: 6px; padding: 12px; font-size: 12px; color: #8892a4; white-space: pre-wrap; word-break: break-word; max-width: 100%; }
52-
p { color: #8892a4; font-size: 13px; }
59+
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; color: #BFBFBF; background: #161617; padding: 24px; line-height: 1.5; }
60+
.brand { display: flex; align-items: center; gap: 10px; margin: 0 0 18px; }
61+
.brand .logo { width: 26px; height: 26px; flex-shrink: 0; background-color: #BFBFBF; -webkit-mask: url("${iconUri}") no-repeat center / contain; mask: url("${iconUri}") no-repeat center / contain; }
62+
.brand h1 { font-size: 18px; font-weight: 600; color: #BFBFBF; margin: 0; }
63+
p { color: #BFBFBF; font-size: 13px; margin: 0 0 8px; }
64+
p.hint { color: #6F6F70; }
65+
code { background: #1E1F20; border: 1px solid #2C2D2E; border-radius: 4px; padding: 1px 5px; font-size: 12px; }
5366
</style>
5467
</head>
5568
<body>
56-
<h2>Claude Code Usage</h2>
69+
<div class="brand">${logo}<h1>Claude Code Usage</h1></div>
5770
<p>${escapeHtml(statusText) || "The dashboard server is not running yet."}</p>
58-
<p>Run <code>Claude Usage: Open Dashboard</code> from the command palette to start it.</p>
71+
<p class="hint">Run <code>Claude Usage: Open Dashboard</code> from the command palette to start it.</p>
5972
</body>
6073
</html>`;
6174
}
@@ -89,14 +102,27 @@ export class DashboardSidebar implements vscode.WebviewViewProvider {
89102
private currentUrl: string | null = null;
90103
private statusText = "";
91104
private readonly onShow: () => void;
105+
private readonly extensionUri: vscode.Uri | undefined;
106+
private iconUri = "";
107+
private cspSource = "";
92108

93-
constructor(onShow: () => void = () => {}) {
109+
constructor(onShow: () => void = () => {}, extensionUri?: vscode.Uri) {
94110
this.onShow = onShow;
111+
this.extensionUri = extensionUri;
95112
}
96113

97114
resolveWebviewView(view: vscode.WebviewView): void {
98115
this.view = view;
99116
view.webview.options = { enableScripts: true };
117+
// Resolve a webview-safe URI for the bundled icon so the status pane shows
118+
// the same logo as the dashboard header. Guarded so node-only tests (whose
119+
// fake view has no asWebviewUri / no vscode.Uri) don't blow up.
120+
if (this.extensionUri && typeof view.webview.asWebviewUri === "function") {
121+
this.iconUri = view.webview
122+
.asWebviewUri(vscode.Uri.joinPath(this.extensionUri, "resources", "icon.svg"))
123+
.toString();
124+
this.cspSource = view.webview.cspSource ?? "";
125+
}
100126
this.render();
101127
view.onDidDispose(() => {
102128
this.view = undefined;
@@ -127,7 +153,7 @@ export class DashboardSidebar implements vscode.WebviewViewProvider {
127153

128154
private render(): void {
129155
if (!this.view) return;
130-
this.view.webview.html = renderHtml(this.currentUrl, this.statusText, makeNonce());
156+
this.view.webview.html = renderHtml(this.currentUrl, this.statusText, makeNonce(), this.iconUri, this.cspSource);
131157
}
132158
}
133159

vscode-extension/test/sidebar.test.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,19 @@ describe("renderHtml with null URL (status pane)", () => {
116116
const html = renderHtml(null, "", NONCE);
117117
expect(html).not.toContain("frame-src");
118118
});
119+
120+
it("renders the logo and an img-src CSP when an icon URI is provided", () => {
121+
const html = renderHtml(null, "", NONCE, "https://host/icon.svg", "vscode-webview://abc");
122+
expect(html).toContain('class="logo"');
123+
expect(html).toContain("img-src vscode-webview://abc");
124+
expect(html).toContain('mask: url("https://host/icon.svg")');
125+
});
126+
127+
it("omits the logo and img-src when no icon URI is provided", () => {
128+
const html = renderHtml(null, "", NONCE);
129+
expect(html).not.toContain('class="logo"');
130+
expect(html).not.toContain("img-src");
131+
});
119132
});
120133

121134
describe("DashboardSidebar onShow auto-start", () => {

0 commit comments

Comments
 (0)