5.8 KiB
5.8 KiB
memory — claude-usage-widget
Durable memory for this project. Read at session start, update before session end. Date format: YYYY-MM-DD.
Decisions & rationale
- Tauri 2 (Rust + Svelte 5 + Vite + TS) over Electron — smaller binary (~10 MB vs ~150 MB), native Windows transparency, real always-on-top z-order. Chose Svelte over React because the widget has only three SVG primitives; React boilerplate isn't worth it.
- Inline SVG, no chart library —
BlockRingis one circle,WeeklyBaris seven<rect>s,ModelStackis a stacked single-row bar. Adding Chart.js / ECharts / uPlot for ~80 lines of SVG would balloon the bundle for nothing. - JSONL-only data source, no Anthropic API — Anthropic doesn't expose a local cap-state file, but the JSONL transcripts contain everything we need to derive usage (this is what
ccusagedoes). Avoids needing an admin key and keeps the widget fully offline. - Widget runs on Windows host, not in WSLg — needs to pin to the Windows desktop, autostart on login, and share the always-on-top z-order with native Windows apps. WSLg windows can't do that. The widget reads WSL transcripts via the
\\wsl$\<distro>\home\<user>\.claude\projects\UNC mount. notifywatcher + 60s tokio poll fallback —ReadDirectoryChangesWon the WSL 9P mount is unreliable; the poll backstops it. 60 s is a pragmatic balance vs CPU.- All filesystem reads happen Rust-side — the JS
capabilities/default.jsondoes NOT granttauri-plugin-fs. Keeps the webview sandbox tight. - Block algorithm —
block_start = floor_to_hour(first_ts_of_block),block_end = block_start + 5h, new block on ≥5h gap OR when previous block ends. This matches ccusage and the way Anthropic's docs describe the rolling window. - Weekly = rolling 7 days, no calendar anchoring — Anthropic's reported Max-plan weekly reset day is buggy and shifts (see GH issues #54974, #52921). The honest thing is "past 7 days from now."
- Caps are user-configurable in Settings with placeholder defaults (200k tokens / 5h block, 2M tokens / week). No authoritative local source for the real caps.
Open questions / TODOs
- Watcher does not re-bind on settings change. If user changes WSL distro override in Settings,
set_settingscallsrefresh_and_emitand updatesstate.roots, but thenotifywatcher is still pinned to the old roots. v0 workaround: restart the widget after changing distro. Better fix: rebuildWatcherHandleon settings change. - Tune cap defaults once we have a few weeks of real data — current 200k / 2M values are guesses.
- Decide whether to expose a tray icon for relaunch after
quit_app(currently the widget can only be reopened via Start Menu / autostart). - Consider whether to fold in the pricing /
$ estimateview later — out of scope for v0 per user. - Verify subagent dedupe assumption: do subagent JSONLs ever contain assistant lines that aren't also in the parent transcript? If yes, we MUST count them; if always duplicate, we MUST skip them. Code uses
requestId || uuidset, which is safe either way. - Replace placeholder Tauri icons in
src-tauri/icons/before release (pnpm tauri icon source.png). - First
cargo check/pnpm installhas not run — toolchain absent in WSL. Build will happen on Windows host; expect minor compile warnings on first try.
Session log
2026-05-08 / 2026-05-09
- Planned the project (approved plan at
~/.claude/plans/snug-mapping-milner.md) and built the full scaffold in one session that crossed midnight UTC. - Authored Tauri config (frameless, transparent, alwaysOnTop, skipTaskbar, 280×360), Cargo.toml, capabilities/default.json.
- Authored Rust modules:
state.rs,settings.rs,paths.rs,jsonl.rs,usage.rs(with unit tests for block boundaries),watch.rs,commands.rs,lib.rs,main.rs. - Authored Svelte 5 frontend:
App,TitleBar,BlockRing,WeeklyBar,ModelStack,Settingscomponents plusipc.ts,types.ts,format.ts,styles.css. - Wrote
scripts/seed-fake-jsonl.ps1verification helper andREADME.mdbuild instructions. - Pushed to Forgejo at
https://git.rdx4.com/megaproxy/claude-usage-widget. (Note: had to rename localmaster→mainafter the fact —git initran before the globalinit.defaultBranch=mainwas set.) - First successful run on Windows host (Doug's machine). Took 4 incremental fixes to get there:
- pnpm 11 default-deny on postinstall scripts → declared
pnpm.onlyBuiltDependencies: ["esbuild"]inpackage.json. - Missing icons → user generated placeholder via PowerShell
System.Drawing+pnpm tauri icon. notify::Watchertrait not in scope (E0599) andtokio::JoinHandle≠tauri::async_runtime::JoinHandle(E0308 ×2) inwatch.rs— fixed in commitab75ca9.tauri-plugin-autostartdoesn't accept a{"args":[…]}block intauri.conf.json— args go through the Rustinit()call only. Removed the JSON entry in commit8c25b01.
- pnpm 11 default-deny on postinstall scripts → declared
- Toolchain (rust/node/pnpm) NOT installed in this WSL environment — that's expected; the build runs on the Windows host.
cargo check/pnpm installnot run from here.
External references
- Forgejo repo: https://git.rdx4.com/megaproxy/claude-usage-widget
- Approved plan:
/home/megaproxy/.claude/plans/snug-mapping-milner.md - Tauri 2 prerequisites: https://v2.tauri.app/start/prerequisites/
- Tauri 2 window customization: https://v2.tauri.app/learn/window-customization/
- Tauri 2 autostart plugin: https://v2.tauri.app/plugin/autostart/
- Tauri 2 capabilities: https://v2.tauri.app/security/capabilities/
- ccusage (algorithm reference): https://github.com/ryoppippi/ccusage
- Claude Max weekly reset issues (context for "rolling 7d" choice): https://github.com/anthropics/claude-code/issues/54974 · https://github.com/anthropics/claude-code/issues/52921