Skip to content

Extract ActivityCard model colors to lib/constants/model-colors.ts - #143

Merged
ohong merged 4 commits into
mainfrom
oh-nightly/model-colors-constants
Aug 30, 2026
Merged

Extract ActivityCard model colors to lib/constants/model-colors.ts#143
ohong merged 4 commits into
mainfrom
oh-nightly/model-colors-constants

Conversation

@ohong

@ohong ohong commented Jul 8, 2026

Copy link
Copy Markdown
Owner

Summary

Roadmap item ("UX Polish" → "Model colors outside design system"): ActivityCard.tsx used hardcoded hex colors inline for model chips. Extracted them to apps/web/lib/constants/model-colors.ts as MODEL_COLOR_PATTERNS (ordered pattern→color list) and MODEL_COLOR_FALLBACK_PALETTE (hash-based fallback palette), matching the existing lib/constants/regions.ts convention.

Pure refactor — same colors, same match order, no behavior change.

Changes

  • apps/web/lib/constants/model-colors.ts — new file with the extracted constants.
  • apps/web/components/app/feed/ActivityCard.tsxmodelColor() now iterates MODEL_COLOR_PATTERNS and falls back to MODEL_COLOR_FALLBACK_PALETTE.
  • docs/CHANGELOG.md — entry under Unreleased/Changed.
  • docs/ROADMAP.md — removed the now-completed item.

Test plan

  • bun run lint — clean
  • bun run typecheck — clean
  • bun run test — 646/646 web tests pass, 199/199 CLI tests pass (unaffected)

Generated by Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Fixed incorrect or missing colors for unknown model labels in activity feeds.
    • Ensured model colors remain consistent and deterministic across displays.
  • Refactor
    • Improved model-specific color handling while preserving the existing appearance for recognized model families.
  • Documentation
    • Updated release notes and roadmap entries to reflect the color-handling improvements.

Pure refactor per the ROADMAP "Model colors outside design system" item —
same colors, same match order, no behavior change.
@vercel

vercel Bot commented Jul 8, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
straude Ready Ready Preview Aug 30, 2026 8:32am

Request Review

@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

Next included review available in 54 minutes.

View limit details

Limit details: You’ve used the included review currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 73d9d387-5a85-4efb-8635-fbbee403b49f

📥 Commits

Reviewing files that changed from the base of the PR and between d98e6eb and e0f4d63.

📒 Files selected for processing (2)
  • docs/CHANGELOG.md
  • docs/ROADMAP.md
📝 Walkthrough

Walkthrough

Model chip color resolution moved from ActivityCard into model-colors.ts. The shared helper preserves ordered model-family mappings and adds deterministic fallback handling. Unit tests cover mappings and fallback behavior. The changelog records both changes.

Changes

Model Color Extraction

Layer / File(s) Summary
Model color resolution helper
apps/web/lib/constants/model-colors.ts
Adds ordered model-family patterns, a fallback palette, and deterministic hash-based selection for unmatched model names.
ActivityCard integration and validation
apps/web/components/app/feed/ActivityCard.tsx, apps/web/__tests__/unit/model-colors.test.ts
ActivityCard uses modelColor. Tests cover known mappings, pattern precedence, fallback colors, repeatability, and empty names.
Changelog updates
docs/CHANGELOG.md
Documents the fallback indexing fix and the extraction of model color selection.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main refactor: moving ActivityCard model colors to lib/constants/model-colors.ts.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch oh-nightly/model-colors-constants

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@ohong ohong mentioned this pull request Jul 8, 2026
Resolves the docs/CHANGELOG.md conflict by folding the model-colors entry
into the existing Unreleased/Changed section rather than opening a second one.

Also fixes a real bug in the extracted code: hashString returns a signed
32-bit int, so a name that hashed negative indexed off the front of the
fallback palette and gave the chip an undefined backgroundColor — about half
of all unknown names, hidden from TypeScript by a non-null assertion. It only
became reachable in production when collection started accepting every
ccusage source, so names like kimi-k2 and deepseek-v3 now hit it.

modelColor and its hash move into lib/constants/model-colors.ts so the module
owns the whole name-to-colour decision instead of exporting raw tables for
callers to recombine, and so it is unit-testable without mounting the card.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/web/lib/constants/model-colors.ts (1)

14-23: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Replace raw hex values with globals.css theme tokens.

These model-chip colors bypass the shared theme. Define or use the required theme colors, then reference their CSS variables from this constant module.

As per coding guidelines, apps/web/**/*.{css,tsx,ts} must use colors from the globals.css @theme block.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/lib/constants/model-colors.ts` around lines 14 - 23, Update
MODEL_COLOR_PATTERNS to replace every raw hex color with the corresponding CSS
variables defined in the globals.css `@theme` block. Add any missing theme tokens
there first, then reference those variables from this constant while preserving
the existing model-pattern mappings and order.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/CHANGELOG.md`:
- Line 7: Update the fallback palette indexing in modelColor() within
model-colors.ts to normalize the signed hash modulo result into a non-negative
index before accessing MODEL_COLOR_FALLBACK_PALETTE. Preserve valid palette
selection for all model names, then retain the changelog entry only once this
implementation fix is applied.

---

Outside diff comments:
In `@apps/web/lib/constants/model-colors.ts`:
- Around line 14-23: Update MODEL_COLOR_PATTERNS to replace every raw hex color
with the corresponding CSS variables defined in the globals.css `@theme` block.
Add any missing theme tokens there first, then reference those variables from
this constant while preserving the existing model-pattern mappings and order.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 666aeaaa-c725-4a7b-8a62-d798ff6bf898

📥 Commits

Reviewing files that changed from the base of the PR and between 6c27bf1 and d98e6eb.

📒 Files selected for processing (5)
  • apps/web/__tests__/unit/model-colors.test.ts
  • apps/web/components/app/feed/ActivityCard.tsx
  • apps/web/lib/constants/model-colors.ts
  • docs/CHANGELOG.md
  • docs/ROADMAP.md
💤 Files with no reviewable changes (1)
  • docs/ROADMAP.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/components/app/feed/ActivityCard.tsx

Comment thread docs/CHANGELOG.md

### Fixed

- **Model chips no longer render colourless for unknown models.** The fallback palette was indexed with a signed 32-bit hash, so any model name that hashed negative indexed off the front of the array and handed the chip an `undefined` background — about half of all names, hidden from TypeScript by a non-null assertion. Now that collection accepts every ccusage source, real names like `kimi-k2` and `deepseek-v3` hit this. The hash is folded to a positive index and the mapping is covered by unit tests.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Do not document the fallback-index fix until the implementation normalizes negative indexes.

apps/web/lib/constants/model-colors.ts still uses hashString(name) % MODEL_COLOR_FALLBACK_PALETTE.length. A negative signed hash produces a negative array property, so modelColor() can still return undefined. ActivityCard then receives an invalid backgroundColor.

Normalize the modulo result before updating this changelog entry.

Proposed fix
-  const index = hashString(name) % MODEL_COLOR_FALLBACK_PALETTE.length;
+  const hash = hashString(name);
+  const index =
+    ((hash % MODEL_COLOR_FALLBACK_PALETTE.length) +
+      MODEL_COLOR_FALLBACK_PALETTE.length) %
+    MODEL_COLOR_FALLBACK_PALETTE.length;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/CHANGELOG.md` at line 7, Update the fallback palette indexing in
modelColor() within model-colors.ts to normalize the signed hash modulo result
into a non-negative index before accessing MODEL_COLOR_FALLBACK_PALETTE.
Preserve valid palette selection for all model names, then retain the changelog
entry only once this implementation fix is applied.

@ohong

ohong commented Aug 9, 2026

Copy link
Copy Markdown
Owner Author

Merged main in (d98e6eb) and found a live bug in the exact code this PR moves, so the extraction now ships a fix rather than a pure relocation.

The fallback palette was indexed with a signed hash. hashString ends each round with hash |= 0, which is a signed 32-bit coercion, so the result is negative for roughly half of all inputs. hash % palette.length then returns a negative index and MODEL_COLOR_FALLBACK_PALETTE[index] is undefined — handed straight to backgroundColor, so the chip renders with no colour. The ! non-null assertion on that lookup is what kept TypeScript quiet about it. Concretely: kimi-k2 hashes to index -4, deepseek-v3 to -4. Fixed by returning Math.abs(hash), with a comment on why the Math.abs is load-bearing so nobody trims it later.

This is reachable in production now, not theoretically. It only bit models that miss every known-family pattern, which used to mean almost nothing since we collected Claude and Codex — but collection takes every ccusage source now, so Gemini, Kimi, DeepSeek and Qwen names all land in the fallback path and roughly half of them come out colourless.

Shape change from the original PR. It exported the two tables and left modelColor plus its hash in ActivityCard, which means every future caller has to know to recombine them the same way, and the interesting logic stays untestable without mounting a card. The module now exports modelColor and owns the whole name-to-colour decision; the tables stay exported for tests and for anyone who needs the raw palette. ActivityCard is down to one import.

Tests: __tests__/unit/model-colors.test.ts, 5 cases. They key off prettifyModel output rather than raw model IDs, because that's what ActivityCard actually passes in — a pattern written against raw IDs would be dead code and a test using raw IDs wouldn't notice. One case pins Fable ahead of Opus, since first-match-wins and those two share the accent family, so a reorder would silently repaint chips.

Conflict was docs/CHANGELOG.md only. Kept main's CLI-scorecard entry and folded the model-colors line into the existing ### Changed block instead of opening a second one.

Typecheck 0, lint 0, 5/5 new tests pass.

@ohong

ohong commented Aug 9, 2026

Copy link
Copy Markdown
Owner Author

CI red here is not this PR. Every open PR is failing the same single assertion:

AssertionError: expected 0.28400000000000003 to be close to 0.355
  ❯ packages/cli/__tests__/ccusage-pricing.integration.test.ts:94:34

That test pins hardcoded LiteLLM dollar amounts for the GPT-5.6 family, and LiteLLM repriced gpt-5.6-terra upstream. 202 of 203 CLI tests pass. #149 fails it too despite touching only .gitignore and deleted artifacts, which is the clearest evidence it is repo-wide.

#150 replaces the pinned rates with the invariant (every model resolves to a non-zero price, day total equals the sum of the breakdown). Merge that first and this should go green on a re-run.

…rs-constants

# Conflicts:
#	docs/CHANGELOG.md
@ohong
ohong merged commit bb2ac16 into main Aug 30, 2026
4 checks passed
@ohong
ohong deleted the oh-nightly/model-colors-constants branch August 30, 2026 09:46
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.

2 participants