Skip to content

feat(profile): model legend, today tooltip, newest-first toggle, client logos - #863

Merged
junhoyeo merged 3 commits into
mainfrom
feat/profile-chart-polish
Jul 12, 2026
Merged

feat(profile): model legend, today tooltip, newest-first toggle, client logos#863
junhoyeo merged 3 commits into
mainfrom
feat/profile-chart-polish

Conversation

@junhoyeo

@junhoyeo junhoyeo commented Jul 12, 2026

Copy link
Copy Markdown
Owner

Four polish fixes on the public profile activity dashboard (all frontend).

1. Usage over time — legend shows models, not clients

The chart is model-based (tooltip lists claude-opus-4-8, gpt-5.5, … each with its own shade) but the legend listed client/provider names (Claude Code, OpenCode, …). New pure helper selectLegendModels(series, limit) ranks the real model series by the selected metric, disambiguates duplicate labels by provider, and the legend now shows the top models with a +N more cutoff. Respects the provider filter automatically.

2. Contributions — "today" now shows a hover tooltip

The newest day is the default-selected cell, and the hover tooltip was suppressed for the selected cell (cell.date === selectedDate) — so hovering today showed nothing. Now the tooltip shows for any in-range cell (2D and 3D), including today/selected. Selection, keyboard nav, and the separate breakdown panel are unchanged. In-range-but-non-selectable cells are no longer disabled, so they're hoverable.

3. Contributions — "Newest first" toggle (localStorage, responsive default)

A persisted checkbox (2D only) mirrors the calendar so recent weeks sit on the left. Default follows layout: reversed on desktop (>=1360px, the 2-column dashboard) and chronological on mobile; an explicit toggle wins and is stored under tokscale:contributions-newest-first. Implemented via an idempotent reverseContributionCalendarWeeks (reverses whole-week chunks + remaps month markers); displayCells/displayMonthMarkers drive both the render and keyboard nav so they can't desync. Hydration-safe: server + first client paint render chronological (isMounted gate + SSR-safe useMediaQuery), then the resolved value applies after mount.

4. Day breakdown — client logos in "Clients and models"

Replaced the colored client dot next to each client name with the client logo (SourceLogo), falling back to the dot when a client has no logo.

Verification

tsc --noEmit clean · eslint clean on all changed files · 587 frontend tests pass (62 files, +new pure-helper tests for selectLegendModels and reverseContributionCalendarWeeks) · production next build passes.

https://claude.ai/code/session_01YTwnnT7dC3tTeLZ5xc4WiZ


Summary by cubic

Polished the public profile activity dashboard with a model-based legend, a “Newest first” 2D calendar option, consistent tooltips, and client logos with better a11y. Improves clarity and navigation without changing data.

  • New Features

    • Legend shows top models by the selected metric, disambiguates duplicate labels with provider, and adds “+N more”.
    • Contributions (2D): persisted “Newest first” mirror so recent weeks are on the left. Default is reversed on desktop (>=1360px) and chronological on mobile; hydration-safe and keeps month markers in sync.
    • Day breakdown: use client logos with a color-dot fallback.
  • Bug Fixes

    • Contributions: hover tooltip now shows for any in-range cell, including “today” even when selected (2D and 3D).
    • Newest-first: auto-scroll brings the newest day back into view after toggle; keyboard navigation stays chronological.
    • Accessibility: client logos in “Clients and models” are marked decorative so screen readers don’t announce the name twice.

Written for commit fef932d. Summary will update on new commits.

Review in cubic

…nt logos

Four polish fixes on the public profile activity dashboard:

- Usage over time: the legend listed clients/providers while the chart is
  model-based. Show the user's top models (by the selected metric) with a
  "+N more" cutoff via a new pure `selectLegendModels` helper.
- Contributions: hovering the newest ("today") cell showed no tooltip because
  it is the default-selected cell and the hover was suppressed for the selected
  cell. Show the tooltip for any in-range cell (2D + 3D); selection/keyboard
  behavior is unchanged.
- Contributions: add a localStorage-persisted "Newest first" toggle (2D) that
  mirrors the calendar so recent weeks sit on the left. Default follows layout —
  reversed on desktop (>=1360px, the 2-column dashboard), chronological on
  mobile. Hydration-safe: server + first paint render chronological, then the
  resolved value applies after mount.
- Day breakdown: replace the colored client dot in the "Clients and models"
  headers with the client logo (SourceLogo), falling back to the dot when a
  client has no logo.

Constraint: reversal must not desync month markers or keyboard nav — both route
through the same display-ordered week structure as the cells
Rejected: CSS scaleX(-1) flip | mirrors month-label text and breaks scroll math
Rejected: apply reversal to the 3D isometric view | geometry mirror is risky and
the request targets the 2D card; 3D stays chronological
Confidence: high
Scope-risk: moderate
Directive: keep displayCells/displayMonthMarkers derivation the single source
for both render and keyboard nav so reversed mode can't desync
Not-tested: real-DOM hydration path (no jsdom); covered via pure-helper tests
@vercel

vercel Bot commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
tokscale Ready Ready Preview, Comment Jul 12, 2026 3:04pm

Request Review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8f4aa7552d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

: null,
[reversed, calendar],
);
const displayCells = displayWeeks?.cells ?? calendar.cells;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Re-scroll after reversing the calendar

When the user enables Newest first in an overflowed 2D calendar, this swaps the target date's DOM position, but the scroll effect below only depends on rangeEnd, rangeStart, tabbableDate, and view. If the calendar was already auto-scrolled to the latest day in chronological order, toggling this leaves scrollLeft at the old right edge, so the newly-leftmost newest weeks can be out of view and the user sees the oldest columns instead. Include the resolved ordering (for example reversed/displayCells) in the scroll effect's dependencies or otherwise adjust scroll on toggle.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — fixed in 85cbd58. The auto-scroll effect now includes reversed in its deps, so toggling Newest first re-runs it and scrolls the newest day's cell (now at the opposite edge) back into view.

Comment on lines +2631 to +2633
onKeyDown={(event) =>
handleCellKeyDown(cell, event, displayCells)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep date-key navigation chronological

Passing displayCells here makes keyboard navigation use reversed week chunks when Newest first is enabled, but getContributionFocusDate still interprets ArrowLeft/ArrowRight as adjacent indices and ArrowUp/ArrowDown as ±7 days. In the reversed layout, pressing ArrowRight from a newest-week Saturday jumps to the Sunday of the previous week rather than the next day, and Home/End are inverted, so keyboard inspection no longer follows the documented day/week boundaries. Keep navigation on chronological calendar.cells or update the helper to navigate by visual coordinates explicitly.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 85cbd58. Rather than reconcile the ±1/±7 arithmetic with the mirrored layout, keyboard navigation now stays on the chronological calendar.cells: Arrow keys inspect adjacent calendar days and Home/End hit the true range boundaries regardless of the visual mirror. Newest first is display-only, so the documented a11y contract is preserved.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 5 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread packages/frontend/src/components/profile/ProfileContributionGraph.tsx Outdated
…gical

Addresses two review findings on the "Newest first" toggle:

- The auto-scroll effect didn't depend on `reversed`, so toggling the mirror
  left scrollLeft at the old edge and the now-leftmost newest weeks could sit
  off-screen. Add `reversed` to the effect deps so the newest cell is scrolled
  back into view after a toggle.
- Keyboard navigation was fed the reversed display order, which scrambled the
  Arrow/Home/End semantics (ArrowRight crossed into the previous week, Home/End
  inverted). Navigate the chronological `calendar.cells` instead: arrows inspect
  adjacent calendar days and Home/End hit the true range boundaries regardless
  of the visual mirror, honoring the documented a11y contract.

Directive: "Newest first" is display-only — do not route keyboard/date nav
through displayCells; keep it chronological
Confidence: high
Scope-risk: narrow
The client logo in the "Clients and models" headers rendered with
alt={sourceId}, so assistive tech announced the source name twice — once from
the image and once from the adjacent visible client name (the prior colored dot
was decorative). Add an opt-in `decorative` prop to SourceLogo (empty alt) and
use it here so the row keeps a single accessible label.

Confidence: high
Scope-risk: narrow
@junhoyeo
junhoyeo merged commit 581c08f into main Jul 12, 2026
7 checks passed
@junhoyeo
junhoyeo deleted the feat/profile-chart-polish branch July 12, 2026 15:07
junhoyeo added a commit that referenced this pull request Jul 12, 2026
…#864)

The reverse-render toggle from #863 landed on the Contributions calendar,
but the request targeted the Usage over time chart — the card that sits in
the right-hand column of the >=1360px dashboard grid. Remove the calendar
mirroring entirely and mirror the usage chart's time axis instead.

The whole per-day pipeline (dates, series values, daily totals) is reversed
once in reverseUsageChartData, so pointer hit-testing, keyboard inspection,
tooltips, and the date-range labels all follow visual order with no special
cases. Trailing averages are computed on chronological data before the
mirror; per-provider cost lookups map the visual index back into the
chronological days array.

Constraint: SSR/first paint must stay chronological (no hydration mismatch);
the resolved default applies only after mount
Constraint: 30d trailing average must be computed pre-reversal
Rejected: reversing only the SVG x-mapping | every index consumer (tooltip,
keyboard, provider costs) would need its own mirror logic
Rejected: sharing one localStorage key with the removed calendar toggle |
stale "0"/"1" from the mistaken feature would silently override the new
responsive default
Confidence: high
Scope-risk: narrow
Directive: keep chartData display-ordered; anything indexing the
chronological `days` array must go through chronologicalActiveIndex
Not-tested: manual toggling while a committed (pinned) inspection is open on
a coarse-pointer device
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant