claude-usage-widget/memory.md

79 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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** — `BlockRing` 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 fallback** — `ReadDirectoryChangesW` 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 `master``main` 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::JoinHandle``tauri::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`.
- **Pivoted from cap estimation to real `/usage` data.** First version showed 999% red ring because the placeholder caps (200k/2M) were wildly under what a Max user actually does. Tier-detection from `.claude.json` improved defaults but still wasn't right; user pointed out they'd previously had an app showing real subscription %. Investigated and found:
- `/usage` slash command output isn't reachable via `claude --print` (LLM intercepts the literal string).
- The data isn't cached anywhere on disk between invocations.
- The OAuth credentials at `~/.claude/.credentials.json` work, but reusing them to call an undocumented endpoint felt fragile.
- **Solution: PTY-drive `claude` itself.** New `src-tauri/src/cli_usage.rs` spawns claude via `portable-pty`, sends `/usage`, parses the three rendered bars (Current session / Current week all / Current week Sonnet). 5-min refresh + manual button. ~3-5s per fetch. (commit `db9a10a`)
- **Bring-up gotchas while wiring the PTY drive on Windows:**
1. Settings UI was non-interactive earlier because of two compounding bugs — a) Save was silently failing because the autostart plugin threw "OS error 2" in dev builds (target\debug exe path is unstable) and we let it abort the save; b) my PowerShell `mouse_event` clicks weren't reaching WebView2 (legacy API on a transparent borderless host), making me think the user's clicks were broken too. Real-mouse clicks worked once Save stopped getting blocked. Fix: best-effort autostart toggle (commit `9786437`).
2. `paths::resolve_roots` was canonicalizing UNC paths to `\\?\UNC\…` form, which broke `Path::parent()` and made tier detection silently fail. Stopped canonicalizing (commit `c5c38d1`).
3. Default `wsl.exe -- claude` invokes a non-interactive non-login shell with no PATH; resolved by defaulting to `wsl.exe -d Ubuntu bash -lc claude` on Windows when wsl.exe is detected (commit `7504990`). The `-d Ubuntu` matters because user's default WSL distro was `docker-desktop` (Alpine; no claude, no bash).
4. Title bar buttons were inside `data-tauri-drag-region`; needed explicit `data-tauri-drag-region="false"` per button so clicks don't get interpreted as drag-start.
- **Final UX polish (this session):** widget made resizable (220×240 min, 300×320 default), inline-SVG ring scales via viewBox, background opacity bumped to 93% so files behind don't bleed through, scrollbar bug from `border + width: 100vw` overflow killed via `box-sizing: border-box` reset + `body { overflow: hidden }` (commits `f90bb3b`, `c38d895`).
- **Status:** widget is live on Windows showing real subscription percentages (72% session at end of session). 18 commits on `main`, all pushed. User is happy.
### 2026-05-09 — v0.1.0 release
- Polish pass for shipping to friends:
- Robust claude command auto-detect: tries native `claude`, then enumerates WSL distros via `wsl.exe -l -q` and probes each via `bash -lc 'command -v claude'`. No more hardcoded `-d Ubuntu`.
- Clear empty-state UI when no claude is reachable (Anthropic install link).
- Real icon: `scripts/make-icon.py` produces a 1024×1024 ring-on-dark monogram. Pillow's multi-res ICO writer was flaky so user re-ran `pnpm tauri icon` to regenerate proper multi-resolution icons.
- System tray icon (left-click = restore window, right-click menu Show/Hide/Refresh/Quit). Solves "lost off-screen" recovery.
- End-user-focused README; build instructions moved to bottom.
- `pnpm tauri build` produced `Claude Usage Widget_0.1.0_x64-setup.exe` (1.7 MB NSIS user-scope installer).
- Created Forgejo release v0.1.0 with the .exe attached.
- Repo flipped to public (per user's explicit request via AskUserQuestion + by trying to do it themselves) so anonymous downloads work.
- Public download URL: https://git.rdx4.com/megaproxy/claude-usage-widget/releases/tag/v0.1.0
- 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
- 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