A Windows desktop widget that shows live stats for running Claude Code sessions as pixel-art characters: one session — one robot (or cosmonaut) at a workstation. The widget lives on the desktop below all windows and stays out of your way.
- Live session monitoring: status (working/idle), model, project, first prompt, tokens (input/output/cache), turn count, current tool, uptime.
- Session cost estimate — computed from token usage per model (5m/1h cache writes priced separately) using an editable price table; shown in the stats dialog and as a total in the header strip.
- Two themes — robots in a server room / cosmonauts in an orbital lab — and two layouts: ROOM (a shared scene with 4 workstations) and LIST (a compact list).
- Characters are animated: they type, read, search, drink coffee and sleep depending on what the session is doing right now; they teleport in and out with a beam effect.
- Click a character for a retro-RPG SESSION STATS dialog (click again to close).
- In-widget settings (gear button): window level, opacity, intervals, autostart.
- Widget-style window: transparent, borderless, at the very bottom of the z-order, survives Win+D (Show Desktop), remembers its position, controlled from the tray.
![]() |
![]() |
Two decoupled layers (see cc-widget-spec.md for details):
Claude Code ──(hooks: SessionStart/UserPromptSubmit/Stop/SessionEnd)──> hook.mjs
│ writes
▼
~/.cc-widget/sessions/<session_id>.json
│ polled + stale files cleaned up
▼
Tauri backend (Rust): list_sessions + transcript parsing
│ invoke() every ~1.5 s
▼
Frontend (TypeScript + canvas pixel-art)
- The collector is a tiny Node script attached to a few rare Claude Code lifecycle hooks; it maintains one status file per session.
- The widget is strictly read-only with respect to Claude Code: tokens and
metadata are gathered by incrementally parsing the JSONL transcripts
(
~/.claude/projects/...). - A session counts as alive while its status file keeps updating or its transcript keeps growing; stale files are cleaned up automatically.
You need Windows 10/11 with Claude Code installed (Node.js is already there since Claude Code requires it).
-
The widget: build the installer (see below) or take a prebuilt one from
src-tauri/target/release/bundle/nsis/and run it. -
The collector hooks (one time):
node collector/install.mjs # add --dry-run to preview the changes
The script copies
hook.mjsto~/.cc-widget/and idempotently merges four hooks into~/.claude/settings.json, backing the file up to~/.cc-widget/backups/first. Existing hooks are left untouched; running the script again duplicates nothing. -
Start the widget and open any Claude Code session — a character teleports onto its workstation within a couple of seconds.
~/.cc-widget/config.json is created on first launch. Everything except the window
position can be edited right in the widget (the gear button in the title bar):
| Key | Default | Description |
|---|---|---|
theme / layout |
robots / room |
theme and layout (title-bar toggles) |
window_level |
bottom |
bottom — on the desktop (survives Win+D), normal, top — above all windows |
opacity |
1.0 |
widget opacity (0.2–1.0) |
poll_interval_ms |
1500 |
session polling period (min 500) |
stale_after_ms |
180000 |
session staleness timeout (min 60000) |
autostart |
false |
launch on login |
autostart_delay_ms |
10000 |
wait before creating the window when launched at login (0–120000) — the desktop/GPU stack needs time to settle, otherwise the webview may come up black |
model_prices |
current API rates | price table, USD per MTok, keyed by model-id prefix (longest match wins); edit in the file directly |
window |
— | last window position (written automatically) |
Cost values are estimates at standard API rates (no Batch/fast-mode modifiers) —
useful as a relative gauge; subscription plans are not billed per token. Prices change
over time: update model_prices when they do (defaults were checked 2026-07-05).
Prerequisites: Node.js 20+, Rust (MSVC toolchain), WebView2 (already part of Windows 11).
npm install
npm run tauri dev # run in dev mode
npm run test:collector # collector tests (node --test)
npm run test:unit # frontend tests (vitest)
cargo test # Rust backend tests (from src-tauri/)collector/ hook collector: hook.mjs, install.mjs + tests
src/ frontend: main.ts, state.ts, format.ts, ui/, sprites.js
src-tauri/ Rust backend: sessions.rs, tokens.rs, config.rs, lib.rs
design_handoff_cc_widget/ design references and the spec
cc-widget-spec.md product/architecture specification
src/sprites.js is the designer-made canvas pixel-art renderer (used as is, the art is
never edited by hand).
npm run tauri buildThe command compiles the release binary (frontend build runs automatically via
beforeBuildCommand) and packages two installers:
| Artifact | Path |
|---|---|
| MSI (WiX) | src-tauri/target/release/bundle/msi/<productName>_<version>_x64_en-US.msi |
| NSIS setup | src-tauri/target/release/bundle/nsis/<productName>_<version>_x64-setup.exe |
Notes:
-
On the first run the Tauri CLI downloads the WiX and NSIS tooling automatically (network access required); subsequent builds are offline.
-
To build just one format:
npm run tauri build -- --bundles nsis(ormsi). -
The product name and version in the artifact file names come from
src-tauri/tauri.conf.json(productName,version). The app identifier (identifier) must stay stable — installers treat it as the upgrade key. -
If you replace the icons (
src-tauri/icons/), force a binary rebuild — the icon is embedded into the exe at compile time and cargo does not track icon changes:(Get-Item src-tauri/build.rs).LastWriteTime = Get-Date npm run tauri build
-
The binaries are unsigned, so Windows SmartScreen may warn on first launch of the installer — choose "More info → Run anyway", or set up code signing for distribution.
- Windows only, and only native Claude Code sessions (
%USERPROFILE%\.claude); WSL and remote sessions are out of scope for v1. - The widget never writes into
~/.claude(apart from the one-time hook wiring done by the collector installer). - The
PostToolUsehook is deliberately not used (process-spawn overhead on Windows) — tool activity is derived from the transcript instead. - A session idle for longer than the staleness timeout disappears from the widget and comes back on its next activity — this is expected behavior.



