All notable changes to TokenWatch are recorded here.
The format follows Keep a Changelog; this project adheres to Semantic Versioning.
Pre-v0.1.0 history is the CCSeva lineage (Iamshankhadeep/ccseva) — TokenWatch forked from v1.3.0.
- Codex models in the "by model" distribution. Today's Codex usage
is now folded into the Dashboard and Analytics model breakdown, grouped
by model from the Codex session logs (
turn_context.model). Gated on the "Show Codex card" toggle. Codex tokens are counted non-cached (total − cached_input) to stay comparable with the Claude breakdown, and per-model percentages now divide by the sum of the model entries so the shares sum to 100% with both agents included. Codex cost shows $0 — the logs carry no dollar amount (plan-based usage);today.totalTokensand the cost card remain Claude-only.
- Persistent main-process log at
~/.tokenwatch/logs/main.log(1 MB rotation). The globaluncaughtException/unhandledRejectionhandlers, startup, single-instance denials, and update/IPC errors now write here — packaged GUI builds previously discarded all console output. A new Open logs button in Settings opens the folder. /statuscalibration for Claude usage. Plan token limits aren't published, so the percentage is an estimate; entering the session % from Claude Code's/statusback-solves your real effective limit so future percentages track it. Settings → Claude plan → Calibrate against /status; clearable anytime.- Status-colored tray icon on Windows/Linux — the mark's accent
turns green / amber / red at the safe / warning / critical
thresholds (run
npm run make-tray-iconsto generate the assets; falls back to the plain icon when absent). - Projected 30-day cost tile in Analytics.
- No more fabricated numbers. With no session history the dashboard showed demo data ($2.45 / 850 tokens / random charts); it now renders an honest zero state.
- Ctrl/Cmd+2 no longer opens a blank screen — the unused
liveroute is gone and the number shortcuts now map to the four visible tabs (1 Dashboard · 2 Analytics · 3 Terminal · 4 Settings). - In-app feedback toasts are shown again. Refresh / settings-save notifications were pushed into a queue that nothing rendered; they now route through the toaster.
- Codex context-window % was nonsensical (cumulative session tokens ÷ context window → e.g. 2890%); it now uses the latest turn's tokens, clamped to 100%.
- Analytics summary tiles now follow the 7d/30d toggle (were hardcoded to 7 days), and the final day's X-axis label is no longer dropped on 30d.
- Codex session parsing moved off the main thread (async file I/O) so
a large
~/.codex/sessionsarchive no longer blocks the tray/UI on its 15-second refresh. - Deterministic teardown on quit — all timers are cleared and the ccusage worker thread is terminated instead of relying on process exit.
- Removed dead code: mock-data generators and the unused
LiveMonitoring/NotificationSystemcomponents.
- Installed version readout next to the "Check for updates now"
button in Settings (
TokenWatch vX.Y.Z), so users can confirm which build they're running without checkingabout://pages or the installer filename. Sourced fromapp.getVersion()via a newget-app-versionIPC.
- Manual check failures no longer double-render as banner + toast.
v0.5.5 suppressed the raw autoUpdater error event during retries,
but the retry wrapper's final emit still painted the red banner —
and SettingsPanel's check-now toast fired for the same event, so
users got the same "GitHub temporarily unavailable" message in two
places. Main now marks check-time failures with
source: 'check'; UpdateBanner filters those out, leaving only the toast. Download and install errors (nosource) still paint the persistent banner.
- 504 banner still flashing despite v0.5.4's silent auto-check.
electron-updater's own
autoUpdater.on('error', …)listener was registered alongside our retry wrapper and fired the red banner before the wrapper could decide whether to surface the failure. Each retry attempt also re-fired the event, so a boot-time 504 could paint the banner up to four times in sequence. Now aupdateCheckInFlightflag gates the global error listener during checks: while true, error events are logged only; the retry wrapper owns the emit decision (silent auto-check → nothing; manual check → friendly message after all retries fail). Download and install errors still surface normally — the flag only coverscheckForUpdatescalls.
- Update check crashes on GitHub 504 — the releases.atom feed
occasionally returns a 5xx / gateway timeout, and a single hiccup
used to flash "检查更新失败 504" across the window. Now:
- transient errors (5xx, ETIMEDOUT, ECONNRESET, ENOTFOUND, EAI_AGAIN) retry with backoff (0s → 2s → 5s → 15s) before giving up;
- automatic checks (the 15 s boot check + every-4 h refresh) are silent on failure — they never paint a banner, only log;
- only manual "check now" clicks surface errors, and the copy is friendlier ("GitHub temporarily unavailable — please try again in a moment") instead of raw HTTP response.
- CI cross-platform install —
npm cifailed onubuntu-latest/macos-latestafter sharp + png2icons were added on Windows; the lockfile didn't list every platform's optional prebuilts (most visibly@emnapi/runtimefor Linux glibc/musl). CI + Release workflows now usenpm install --no-audit --no-fundso each runner pulls its own platform binaries. v0.5.2 didn't actually ship installers because of this; v0.5.3 carries the same payload plus the CI fix.
- Dark mode end-to-end coverage. v0.5.1 swept component-level
hardcoded colors, but
tailwind.config.jsstill resolved the named palette (bg-parchment,text-claude-black,bg-terracotta, etc.) to literal hex values, so anyclassName="bg-parchment"was frozen at its light-mode colour. Every Claude palette token now resolves to its matchingvar(--…)CSS variable (defined in:rootfor light and.darkfor the override), so the entire app flips in one place. - Added shadcn semantic aliases (
background,foreground,primary,secondary,muted,accent,destructive,border,input,card,popover) that map to the Claude palette — this lights up the Button / Alert / Progress / Slider / Card / Select / Sonner primitives that were already using shadcn class names. - Pre-hydration inline script in
index.htmlapplies the.darkclass before React mounts, so the first paint is the correct theme — no white flash when the user's preference is dark. - Swept the last few leftovers: MiniHud translucent background, Dashboard hero ring, Analytics chart gridlines, Dashboard popover body copy, Mini HUD ring track, shadcn Switch track, Tabs hover colour, Select item text.
- "Check for updates now" silent failure — the button fired an IPC
and then showed nothing on the happy path.
UpdateBanneronly rendered foravailable / downloading / downloaded / error, so a successful "already on latest" result was swallowed and the user interpreted it as broken. The button now toasts a checking spinner, then success ("you are on the latest version"), error, or falls silent only when the banner itself will take over. - Dark mode hardcoded colours — swept 60+ occurrences across
Dashboard / Analytics / LiveMonitoring / SettingsPanel /
ErrorBoundary / CodexCard / Tooltip / Select / Popover / Switch
that used
bg-neutral-900/80,text-white,#faf9f5,#141413etc. and replaced them with the semantic CSS variables (var(--ivory),var(--claude-black),var(--cream), etc.) so they flip correctly when the theme switches. Terminal view is intentionally left dark — it's designed as a terminal panel. - CI lockfile — sharp / png2icons / vitest installations on
Windows did not record their Linux-specific optional deps in
package-lock.json. Regenerated with--include=optionalsonpm cionubuntu-latestsucceeds.
- Dark mode — warm ink dark palette ("library at dusk", not neon
black) driven by
next-themes. Settings → Theme lets users pick System / Light / Dark. Preference persists tolocalStorageastokenwatch-theme. - Automated tests (Vitest) — 17 tests across
ccusage-utils(timezone-aware ISO formatting, plan detection, token limits, burn rate) andSettingsService(load/save round-trip, partial merge, backfill from defaults, corrupted-file recovery). CI now runsnpm teston every push and PR. - CHANGELOG.md — Keep a Changelog format, back-filled from v0.1.0.
- README — dedicated Linux install section, updated cross-platform badges, clarified unsigned-binary warnings for all three OSes.
- Extracted pure helpers (
toISOStringLocal,detectPlan,getTokenLimit,calculateBurnRate) fromCCUsageServiceintoccusage-utils.tsso they can be unit-tested without spinning up the singleton or a worker thread.
- Cross-platform release pipeline: the GitHub
Releaseworkflow now builds Windows (NSIS installer + portable exe), macOS (universal dmg — x64 + arm64), and Linux (AppImage) in parallel on everyv*tag and attaches all artifacts to the same GitHub Release. scripts/make-icons.mjsnow also emitsassets/icon.icnsviapng2icons(pure JS — works on Windows / Linux CI with no Apple toolchain) plus a 1024 pxassets/icon.pngfor Linux AppImage hicolor expansion.dist:linuxnpm script.
assets/icon.icnshad stayed on the pre-V13 logo; macOS builds now ship the correct brand mark.electron-builder.jsonlinux.iconswitched from.icnsto.png.
- New brand identity: V13 Tally Five — four ink verticals crossed by a terracotta diagonal, mapping directly to "tokens accruing". Replaces the placeholder terracotta "T" disc across the app, tray icon, installer icon, and loading screen.
assets/logo.svgcanonical source +scripts/make-icons.mjssharp-based generator with per-size stroke tuning (16 px tray stays legible, 1024 px installer stays clean).
- Header lockup is now a single editorial line: logo · title · tagline on one baseline; tighter gap; 40 px SVG mark.
- Codex CLI monitoring (opt-in). A second dashboard card shows OpenAI
Codex rate limits (primary 5 h + secondary 7 d windows), context window
utilisation, and last-session tokens. Reads
~/.codex/sessions/**/*.jsonldirectly — no estimation, no API calls. Off by default; toggle in Settings. - Auto-updates via
electron-updater: checks GitHub Releases on launch and every 4 h, shows an update banner, and never starts a download without user consent. Settings panel has a manual "check for updates now" button.
electron-builderno longer tries to auto-publish during localdistruns — avoids the "GitHub Personal Access Token is not set" CI prompt. CI usessoftprops/action-gh-release@v2for attachment.
- Full rename and rebrand: CCSeva → TokenWatch. New app identifiers, Windows AppUserModelID, tray/window titles, i18n copy.
- Complete UI redesign in the Claude / Anthropic warm-parchment design language: serif display type, terracotta accents, ring shadows instead of drop shadows, parchment canvas.
- Frameless window with custom minimise / maximise / close controls drawn in React; whole header strip is a drag region.
- Mini HUD: opt-in always-on-top floating panel (220 × 64 px, top-right by default, draggable). Three content modes — percentage only, +cost, +cost+burn rate.
- Timezone-aware "today" aggregation (previous UTC rollover could empty the model distribution card around midnight).
- Double circular ring in the Dashboard hero — usage percentage + reset countdown, same visual component, colour-tinted per state.
- i18n: English + 简体中文 with auto-detect via
navigator.language. - GitHub Actions:
ci.yml(Ubuntu lint/typecheck/build) +release.yml(Windows installer build onv*tags). - Single-instance lock (
app.requestSingleInstanceLock) — relaunching surfaces the existing window instead of stacking tray icons. - Launch-on-startup and standalone window toggles.
- Windows port with EPIPE-safe stdout/stderr guards for detached shells.
- Worker-thread ccusage parsing — session block parsing runs in a
worker_threadsworker so the tray and UI stay responsive while large histories load. - Three ccusage patches (
patches/ccusage+18.0.8.patch): skip the redundantsortFilesByTimestampcall, parallelise file reads with a boundedPromise.all(32 concurrent), and disk-cache LiteLLM pricing at~/.tokenwatch/pricing-cache.jsonwith a 24 h TTL. ~4× cold-start speedup on large usage histories. - Persistent stats cache at
~/.tokenwatch/stats-cache.jsonso a cold start paints the full UI from disk immediately, then refreshes in the background. - Request coalescing via an in-flight promise + 20 s in-memory cache.
- From Iamshankhadeep/ccseva v1.3.0. MIT licence preserved.