claude-usage-widget/memory.md

6.6 KiB
Raw Blame History

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 libraryBlockRing is one ring, WeeklyBar is two stacked progress bars, ModelStack is a single segmented bar. Adding Chart.js / ECharts / uPlot for ~80 lines of SVG would balloon the bundle for nothing.
  • Subscription %s come from PTY-driving claude /usage (NOT JSONL estimates, NOT the Anthropic API). The widget spawns claude via portable-pty, sends /usage, parses the three rendered bars (Current session / Current week all / Current week Sonnet), and shows those exact numbers. This is the same data Anthropic shows you in the CLI — no API key, no admin scope, no reverse-engineering of their backend. The trade-off is ~3-5 s per refresh and brittleness if Anthropic changes the rendered output format. Refresh every 5 min by default; manual refresh button on the title bar.
  • Per-model breakdown still comes from local JSONL — the CLI's /usage doesn't break out Opus/Sonnet/Haiku, but our token-summing does. ModelStack remains.
  • 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. JSONL transcripts read via \\wsl$\<distro>\home\<user>\.claude\projects\ UNC mount; the PTY-driven claude is invoked via wsl.exe -d Ubuntu bash -lc claude (default on Windows when wsl.exe is on PATH).
  • notify watcher + 60s tokio poll fallbackReadDirectoryChangesW on the WSL 9P mount is unreliable; the poll backstops it.
  • All filesystem reads happen Rust-side — the JS capabilities/default.json does NOT grant tauri-plugin-fs. Keeps the webview sandbox tight.
  • Block algorithm (still in code, used for ModelStack only)block_start = floor_to_hour(first_ts_of_block), block_end = block_start + 5h, new block on ≥5h gap OR when previous block ends. ccusage-equivalent.
  • DROPPED: caps + tier-detection UI. Replaced by real CLI percentages. Caps struct still exists in code as a deprecated fallback but the Settings panel no longer exposes it.

Open questions / TODOs

  • Watcher does not re-bind on settings change. If user changes WSL distro override in Settings, set_settings calls refresh_and_emit and updates state.roots, but the notify watcher is still pinned to the old roots. v0 workaround: restart the widget after changing distro.
  • /usage parser is fragile to output format changes. If Anthropic changes the rendered text (relabels sections, adds new ones, changes "X% used" pattern), the bars stop parsing silently. Settings panel exposes the raw output for debugging when this happens.
  • /usage spawn cost is ~3-5s on Windows. That's per refresh; default refresh is 300s so net overhead is fine. Title-bar refresh button gives user control. Consider caching to disk so cold start has something before the first PTY drive completes.
  • Autostart toggle fails in dev builds (target\debug\ exe path is unstable). Currently swallowed as a warning; needs proper testing once we ship the NSIS bundle.
  • The default WSL-on-Windows command assumes Ubuntu. Auto-detect could iterate wsl.exe -l -q for any distro that has claude on its login PATH, instead of hardcoding -d Ubuntu.
  • Decide whether to expose a tray icon for relaunch after quit_app (currently the widget can only be reopened via Start Menu / autostart).
  • Window background is too transparent — files behind it bleed through visibly. Bump --bg opacity from 0.78 to ~0.92.
  • Replace placeholder Tauri icons in src-tauri/icons/ before release (pnpm tauri icon source.png).
  • Caps struct + Caps::default() are dead code now — delete after a few releases of stability.

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, Settings components plus ipc.ts, types.ts, format.ts, styles.css.
  • Wrote scripts/seed-fake-jsonl.ps1 verification helper and README.md build instructions.
  • Pushed to Forgejo at https://git.rdx4.com/megaproxy/claude-usage-widget. (Note: had to rename local mastermain after the fact — git init ran before the global init.defaultBranch=main was set.)
  • First successful run on Windows host (Doug's machine). Took 4 incremental fixes to get there:
    1. pnpm 11 default-deny on postinstall scripts → declared pnpm.onlyBuiltDependencies: ["esbuild"] in package.json.
    2. Missing icons → user generated placeholder via PowerShell System.Drawing + pnpm tauri icon.
    3. notify::Watcher trait not in scope (E0599) and tokio::JoinHandletauri::async_runtime::JoinHandle (E0308 ×2) in watch.rs — fixed in commit ab75ca9.
    4. tauri-plugin-autostart doesn't accept a {"args":[…]} block in tauri.conf.json — args go through the Rust init() call only. Removed the JSON entry in commit 8c25b01.
  • Toolchain (rust/node/pnpm) NOT installed in this WSL environment — that's expected; the build runs on the Windows host. cargo check / pnpm install not run from here.

External references