feat: year-calendar heatmap (GitHub-style 365-day grid)#29
Conversation
|
Force-pushed: cleanly rebased on current What lands:
Coexists cleanly with everything currently in |
|
Fixed a merge artifact in the embedded JS template: the
Moved the IIFE back to module top-level (right before the final |
e34d42d to
8e760d5
Compare
Adds a contribution-graph-style 'Activity (last 365 days)' card to the dashboard. The grid is 53 cols x 7 rows (Sun-top), with 4 discrete intensity buckets based on daily cost relative to the year's peak day. - dashboard.py: new _year_calendar(conn, today=None) helper aggregates turns + cost server-side per day from the canonical PRICING table; non-billable models still count toward the day's turn total. - API payload exposes the 365-day series under 'year_calendar' so the front-end can render it regardless of the active Models/Range filter. - HTML/CSS card + renderYearCalendar() JS renderer with hover tooltips and a Less/More legend that derives its colour from --accent. - tests/test_year_calendar.py: 10 tests covering empty-DB shape, window boundaries, seeded cost/turn values, unbillable-model handling, the out-of-window cutoff, the API key presence, and the HTML/JS wiring.
8e760d5 to
dc1bdc7
Compare
What does this add and why do you believe it belongs in this dashboard?
Adds a GitHub-style 53x7 year-calendar heatmap covering the trailing 365 days of Claude Code activity, colored by daily cost intensity. The point of a personal usage dashboard is to give you a feel for your own habits at a glance; the existing daily bar chart answers "how much did I spend this week," but not "when did I actually use this over the past year?" A year heatmap is the canonical idiom for that — every developer already reads it on GitHub, so zero learning curve. The trailing-365-day window deliberately ignores the Range/Models filter (the value of the year view is the year view itself; filters would defeat it). Cells are bucketed into four intensity steps using the existing
--accenttheme color, so it adapts to every theme. Tooltips show date, cost, and turns. Cost is calculated server-side from the canonicalpricing.pytable, so unbillable models contribute turns but zero cost — matching the JScalcCosthelper. Click-to-drill (filtering the rest of the dashboard to the clicked day) is a natural next step but intentionally out of scope here to keep this a single concern.Checklist
Code correctness
calcCost()calls pass 6 arguments:(model, inp, out, cache_read, cache_creation, cache_1h)`), not escaped ones (\`)Tests
python3 -m unittest discover -s tests -v— all passingpython3 -m unittest tests.test_browser -v— all passingScope
dashboard.py,scanner.py,cli.py,pricing.py,cowork.py,tests/) — or I've explained below why a new file is needed