Repository navigation
Fix webview/status bar getting stuck on "Loading…" - #20
Merged
Carl723000 merged 1 commit intoJun 15, 2026
Merged
Conversation
The auto-refresh timer and file watcher both call refreshData(), which re-read every JSONL file from scratch. Each call replaced the whole webview with a spinner and flipped the status bar to "Loading…". With no re-entrancy guard, overlapping slow reloads during active Claude Code use kept re-asserting the spinner, so the panel was effectively stuck loading. - Add an isRefreshing guard so a trigger arriving mid-load is coalesced instead of starting a second full reload. - Only show the full-screen spinner on the first load (empty cache). Background refreshes keep the existing dashboard/status bar visible and swap in fresh data when ready. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Carl723000
added a commit
to Carl723000/ClaudeCodeUsage
that referenced
this pull request
Jun 3, 2026
…ner, iOS toggle Summary of all changes landing in the v2.0.1 release: Fixes - Dedup now keeps the higher-token record when hashes collide (issue ClaudeCodeUsage#18, @zhaoxiao9302). Proxy "placeholder + real" pairs are handled correctly. - DeepSeek pricing: deepseek-chat and deepseek-reasoner corrected from Flash rates to Pro rates ($0.435/$0.87). Added deepseek-v4-pro entry. - Quota indicator no longer hides when workspace has no local Claude data. - Webview / status bar no longer gets stuck on "Loading…" (PR ClaudeCodeUsage#20, @nickearnshaw): re-entrancy guard + spinner only on cold start. - Log timestamps now use local time (not UTC). Added - Dashboard "Auto-refresh" toggle (iOS-style) with localised label. When off: status bar still updates, webview pauses until "Refresh Now". - claudeCodeUsage.fileWatching setting (opt-out fs.watch). - claudeCodeUsage.pauseDashboardRefresh setting. - Opus 4.8 explicit pricing entry. - Diagnostic output channel: per-refresh stats + per-model record/token counts. Docs - README intro rewritten to "The Claude Code coach in your status bar" slogan (all language variants updated). - New Troubleshooting entries: cleanupPeriodDays for history retention (PR ClaudeCodeUsage#21, @nickearnshaw) and sub-agent token under-counting note. - Credits updated for PRs ClaudeCodeUsage#20, ClaudeCodeUsage#21, ClaudeCodeUsage#22 (@nickearnshaw). Dev - launch.json preLaunchTask fixed for single-root checkout (PR ClaudeCodeUsage#22, @nickearnshaw).
Carl723000
added a commit
that referenced
this pull request
Jun 3, 2026
…#23) * chore(pricing): add Opus 4.8 to the pricing table Assumed at the current Opus tier rate ($5 / $25 input/output per MTok, $6.25 cache-write, $0.5 cache-read), same as 4.7 / 4.6 / 4.5. Family-aware fallback would already pick the Opus tier for unknown opus-4-* snapshots, but an explicit entry is cleaner and keeps the "Per-model rates" line in the dashboard accurate. If Anthropic differentiates Opus 4.8 pricing later, override here or just hit the "Refresh Token Pricing" button to pull live LiteLLM data. * fix: keep the higher-token record on uniqueHash collision + correct DeepSeek pricing Issue #18 (zhaoxiao9302): when a proxy like mimo or CC Switch writes two records per message — a tokens=0 placeholder when streaming starts and the real values after the response completes — both records share the same messageId and lack a requestId, so they hash identically. The previous dedup kept whichever appeared first, which was almost always the zero-value placeholder, leading to massively under-reported usage (the reporter measured \$0.20 displayed vs. \$7.98 actual on a single day; 185 of 188 today-records had this pattern). Fix: switch processedHashes from Set<string> to Map<string, number> (hash → records[] index). On a duplicate hash, compare the new and existing total token sums and keep whichever is higher. This naturally resolves the placeholder/real pair regardless of arrival order, without needing to detect the proxy itself. DeepSeek pricing was also wrong: `deepseek-chat` and `deepseek-reasoner` were priced at V4-Flash rates (\$0.14 / \$0.28 per MTok) even though the `deepseek-chat` alias has pointed to V4-Pro since 2024-09. They are now priced at the Pro tier (\$0.435 / \$0.87 / cache hit \$0.003625), verified against https://api-docs.deepseek.com/quick_start/pricing/. Added a separate `deepseek-v4-pro` entry. The family fallback for unknown DeepSeek snapshots now also resolves to the Pro tier. This is the same root cause behind the under-reported DeepSeek usage seen by users running CC Switch. Reported-by: @zhaoxiao9302 (issue #18) * fix: decouple quota from local data + opt-out file watcher + shorter cache Three related fixes for the quota indicator and the file watcher: 1. Quota now refreshes unconditionally at the start of every refresh cycle and is no longer hidden when the workspace lacks Claude history or when the data directory cannot be found. The quota is account- level, so a fresh workspace should not affect it. 2. Quota fetch cache shortened from 2 min to 30 s, so switching between VS Code windows no longer shows a stale percentage for up to two minutes. 3. New setting `claudeCodeUsage.fileWatching` (default: true) — when disabled, falls back to interval-based refresh only. Closes issue #17 (Rissew): constant refreshes during active token use can be distracting, this gives users the calmer mode on demand. StatusBarManager.updateUsageData() no longer touches the quota item: the caller (extension.refreshData) calls updateQuota() separately at the very start of the cycle. This change is callback-compatible — the optional usageLimits arg still works for callers wanting a single update path. Closes issue #17. * feat: HTML+SVG progress-bar quota tooltip Replace the Markdown table tooltip with an HTML <table> that embeds inline SVG progress bars per row, mirroring what the official `/usage` command shows. The bar fill colour changes by threshold to match the status-bar warning/error backgrounds: - green at <80% ("plenty left") - amber at >=80% ("getting close") - red at >=95% ("at the limit") SVG was chosen over CSS-styled <div>s because VS Code's MarkdownString sanitiser strips most `style` attributes but lets SVG through intact. This means the bars render correctly on both light and dark themes without needing theme-aware CSS variables. The percentage and reset countdown columns are kept for users who prefer the precise number; the weekly row still uses <br> to put the reset moment and the countdown on two lines. * fix: relax validator, restore 120s quota cache, HTML+span progress bars Four small follow-ups from RC testing: - Bump package.json to 2.0.1 so the packaged .vsix has the right name during the RC cycle. - Quota cache: 30 s → 120 s. Multi-workspace sync was the goal of the shorter window, but 30 s feels too chatty during active token writes; 120 s matches the polling interval. - Relax validateUsageRecord. The previous version dropped any record whose secondary fields had an unexpected type (e.g. `model` is null, `requestId` is a number). That likely cost us records from new Claude Code features (xhigh / ultracode / workflow) and from some proxies that write slightly atypical JSON. Now only the two real must-haves are required: timestamp + numeric input/output tokens. All other fields are treated as best-effort. Added validationDropReason() and a stats counter so the loader writes a one-line "X files, Y lines, kept=N, dedup-replaced=…, rejected: …" summary to the "Claude Code Usage" output channel on every refresh. This lets us see exactly which validator rule is rejecting records rather than guessing. - Quota tooltip progress bars: SVG was being stripped by VS Code's markdown sanitiser (so the new look never reached users). Switched to coloured <span>s with non-breaking-space padding — pure HTML, background-color is in the safe-CSS allowlist, renders consistently. Same colour thresholds as the status-bar background (green / amber at 80% / red at 95%). * fix(tooltip): wider bar, 70% height, gray 100% frame, 24h weekly reset Small polish on the quota tooltip from RC testing: - Bar widened from 16 to 24 segments so low percentages (5h:24%) read proportionally instead of "barely a sliver". - Bar height compressed via font-size:70% on the outer span so the bar looks like a thin strip rather than a full-height block. - Outer span now has a gray 100%-width background — the "track" / frame is always visible regardless of fill, so users can see where the bar ends and where the boundary is. - Weekly reset time switched to 24-hour format (hour12:false) to suppress AM/PM that some locales add. "Wed 03:00" stays "Wed 03:00". * fix(tooltip): rounded corners, visible gray frame, 60% compression + diagnostics model breakdown Tooltip polish (round 2): - border-radius: 4px on both bar layers for rounded corners. - Gray frame opacity bumped from 0.30 -> 0.55 so the 100% boundary is actually visible (previous version disappeared on most themes). - font-size 70% -> 60% — bar reads as a thin horizontal strip rather than a full-line block. Loader diagnostics now also list every model name found, sorted by record count. This makes it instantly obvious whether a proxy is writing records under a non-default model name (e.g. CC Switch's deepseek-v4-pro[1m]) so we can investigate without guessing. * fix(tooltip+diag): visible bar border + 48% height; log token-sum per model Two follow-ups from RC testing round 4: - Quota tooltip: - Bar height further compressed (font-size 60% → 48%) so the bar reads as a thin strip; height is now ~80% of what RC round 3 showed. - Added a 1px gray border on the outer span so the 0-100% frame is visible at all fill levels (the rgba track alone was easy to miss on busy backgrounds). Inner fill keeps 3px radius for soft corners. - Loader diagnostic: model summary now reports token sum per model in addition to record count, formatted as e.g. `deepseek-v4-flash=99/0`. A model with N records but 0 total tokens is conclusive proof that every record for that model was a proxy zero-token placeholder — the pattern behind the missing-Flash story in CC Switch sub-agent paths. * fix: visible tooltip frame, local-time logs, pause-dashboard-refresh setting Three RC-round-5 improvements bundled as one meaningful commit (in line with the "every commit should pull its weight" rule): 1. Quota tooltip progress bar: The previous outer-span + rgba border was invisible under both light and dark themes. Rebuilt as four adjacent solid-coloured spans: `[dark-gray bookend][filled cells][light-gray empty][dark-gray bookend]`. The dark-gray (#666) bookends mark 0% and 100% with strong contrast on any theme; the light-gray (#cccccc) empty fill keeps the unfilled range obvious. Bar colour by threshold (green / amber / red) unchanged. 2. Diagnostic log timestamps now use the local timezone instead of UTC (`toLocaleTimeString(undefined, { hour12: false })`). Affects both the loader and the API client output channel lines. 3. New `claudeCodeUsage.pauseDashboardRefresh` setting (default false). When on, fs.watch and the polling timer skip the webview update; the status bar still refreshes so today's cost and quota stay live. The "Refresh Usage Data" command always force-updates the webview so users can pull a fresh view on demand. Addresses the issue #17 follow-up — agents writing to logs no longer thrash an open dashboard. * feat(dashboard): in-page auto-refresh toggle; tooltip uses solid gray track Two improvements in one commit: 1. Dashboard now has an "Auto-refresh: ON/OFF" toggle in the header. Default state mirrors the `claudeCodeUsage.pauseDashboardRefresh` setting. Clicking the toggle: - flips the CSS class on <body> (auto-off) so the conditional "Refresh Now" button appears in the header - persists the new state by updating the setting When OFF, the status bar still refreshes; the dashboard waits for the user to press "Refresh Now" or run the command. This addresses issue #17's follow-up comment — agents writing to logs no longer thrash an open dashboard while still keeping the user in control. 2. Quota tooltip progress bar reverted to the previously-rendering nested-span structure (the bookend approach didn't render in testing). Outer track is now solid #bbb instead of rgba — solid light-gray is visible on both light and dark themes without relying on opacity that some themes wash out. Inner colour fill sits on top with green/amber/red thresholds unchanged. If the solid-gray track is still not visible enough, the path forward is a v2.1 redesign rather than another small CSS tweak — this commit is the last cosmetic attempt for v2.0.1. * feat(dashboard): iOS-style auto-refresh toggle + i18n for label Replace the previous text button with a proper switch: - A native-looking <label> with an iOS-style slider (32x18 px). Slider turns green (#34c759) when auto-refresh is on, gray when off. Knob slides across via CSS transition. Focus ring uses the VS Code focus border colour. - Switch state stays in sync with `claudeCodeUsage.pauseDashboardRefresh` and survives reload. The "Refresh Now" button only shows when the switch is off (controlled by the .auto-off body class). - New i18n key `autoRefresh` translated for all six languages: EN "Auto refresh", DE "Auto-Aktualisierung", zh-TW "自動刷新", zh-CN "自动刷新", JA "自動更新", KO "자동 새로고침". Replaces the hardcoded English label. Tooltip stays at 4e3f238 (nested-span with solid #bbb gray); further cosmetic work deferred to v2.1. * fix(v2.0.1): dedup, DeepSeek pricing, quota always-show, loading spinner, iOS toggle Summary of all changes landing in the v2.0.1 release: Fixes - Dedup now keeps the higher-token record when hashes collide (issue #18, @zhaoxiao9302). Proxy "placeholder + real" pairs are handled correctly. - DeepSeek pricing: deepseek-chat and deepseek-reasoner corrected from Flash rates to Pro rates ($0.435/$0.87). Added deepseek-v4-pro entry. - Quota indicator no longer hides when workspace has no local Claude data. - Webview / status bar no longer gets stuck on "Loading…" (PR #20, @nickearnshaw): re-entrancy guard + spinner only on cold start. - Log timestamps now use local time (not UTC). Added - Dashboard "Auto-refresh" toggle (iOS-style) with localised label. When off: status bar still updates, webview pauses until "Refresh Now". - claudeCodeUsage.fileWatching setting (opt-out fs.watch). - claudeCodeUsage.pauseDashboardRefresh setting. - Opus 4.8 explicit pricing entry. - Diagnostic output channel: per-refresh stats + per-model record/token counts. Docs - README intro rewritten to "The Claude Code coach in your status bar" slogan (all language variants updated). - New Troubleshooting entries: cleanupPeriodDays for history retention (PR #21, @nickearnshaw) and sub-agent token under-counting note. - Credits updated for PRs #20, #21, #22 (@nickearnshaw). Dev - launch.json preLaunchTask fixed for single-root checkout (PR #22, @nickearnshaw).
Collaborator
|
Thanks @nickearnshaw — this landed in v2.0.1. Closing as released. Really appreciate the clear write-up. |
Collaborator
|
This is now merged for real, @nickearnshaw — sorry it originally showed as "closed". Thanks again! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
During active Claude Code use the usage panel (and the status bar) often gets stuck showing the "Loading…" spinner and never renders the dashboard.
Root cause
refreshData()is triggered by both the auto-refresh timer (≥30s) and the file watcher (1.5s debounce, which fires repeatedly while Claude Code is writing logs). On every trigger:setLoading(true)replaces the entire webview HTML with the spinner and flips the status bar to$(sync~spin) Loading….refreshData()then re-reads and re-parses every JSONL file from scratch — seconds on a large history.setLoading(true)keeps re-asserting the spinner around the time each slow load finishes → effectively perpetual "Loading…".Fix
Two small, data-behaviour-preserving changes in
src/extension.ts:isRefreshing): a trigger that arrives while a load is in flight is coalesced instead of starting a second full reload on top of the first.updateData()/updateUsageData()calls.Error and no-data paths are unchanged and still surface their messages.
Testing
npm run compileis clean.Claude Code Usage: Refresh Usage Dataor use Claude Code so logs keep changing. Before: panel flickers to / sticks on the spinner. After: the dashboard stays visible and updates in place; the spinner only appears on the very first open.🤖 Generated with Claude Code